Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
29230 Discussions

Access 2D Fortran array from C - odd behaviour

gib
New Contributor II
875 Views

This topic must have been beaten to death, but the issue I've encountered seems very odd.

Here is the C code:

void test_array(int *, int *, double **); // To test accessing a Fortran 2D array.

void test_get_array()

{
    int i;
    int narr1, narr2;
    double *farr;
    test_array(&narr1, &narr2, &farr);
    for (i=0;i<narr1*narr2; i++)
        printf("%d %f\n",i,farr[i]);
}

 

and the Fortran:

subroutine test_array(narr1, narr2, cptr) bind(C)
!DEC$ ATTRIBUTES DLLEXPORT :: test_array
use, intrinsic :: iso_c_binding
integer(c_int) :: narr1, narr2
TYPE (C_PTR)    :: cptr
real(c_double), allocatable, target :: a(:,:)
integer :: i1, i2
integer :: n1=4, n2=3

allocate(a(n1,n2))
do i1 = 1,n1
    do i2 = 1,n2
        a(i1,i2) = i1 + 10*i2
    enddo
enddo

narr1 = n1
narr2 = n2
cptr = c_loc(a)
write(*,'(4f6.0)') a

end subroutine

This is the output:

11. 12. 13. 14.

21. 22. 23. 24.

31. 32. 33. 34.

0 0.000000

1 12.000000

2 13.000000

3 14.000000

4 21.000000

5 22.000000

6 23.000000

7 24.000000

8 31.000000

9 32.000000

10 33.000000

11 34.000000

 

What has happened to the first array entry?

 

Hang on!  I just remembered seeing something about SAVE.  When I add that to the declaration of a(:) I get the first entry correctly.  I'm guessing that not SAVEing immediately clobbers the start of the array somehow on subroutine return.  I decided to leave the post here because the phenomenon is interesting and I'd like to see comments on it.

 

Thanks

Gib

0 Kudos
5 Replies
Steven_L_Intel1
Employee
875 Views

Not putting SAVE on a local ALLOCATABLE array causes it to be automatically deallocated on routine end. None of the storage is defined anymore.

0 Kudos
gib
New Contributor II
875 Views

Thanks Steve, it makes sense now.  It was just a matter of chance that the rest of the array entries were still there.

0 Kudos
jimdempseyatthecove
Honored Contributor III
875 Views

Gib,

From a functional point of what your code was trying to do, using SAVE is not necessarily the correct action. The correct action would be to declare the array a as pointer. This way the allocated object is not auto de-allocated upon return (same as SAVE), however, your C code is free to call the routine to obtain an additional object. If your code is only interested in allocating and manipulating a single object then use SAVE. But then as written, there is no way to later reclaim the memory by way of DEALLOCATE. Note, while use of pointer will provide for persistent allocatable objects you cannot use the Fortran pointer to an array as a C pointer to the blob of data. You'd still use the cptr = c_loc(a), but then this would lose the address of the Fortran array descriptor, which would be necessary for the deallocation. For this, your C interface should add a void* as an additional argument that receives the address of the array descriptor. The Fortran routine would then need to be coded to return the reference to the pointer to the array.  

Jim Dempsey

0 Kudos
gib
New Contributor II
875 Views

Hi Jim,

When I was originally flailing around trying to find out how to do this, getting confused by some misleading online information, one variation I tried used an allocatable array - this is the example I posted.  I realised immediately after posting that it works fine with a static array (with SAVE).  In my actual usage, the array will be global, and SAVEd automatically, and deallocation will not be an issue.  Thanks for explaining pointer vs. allocatable.

Gib

0 Kudos
IanH
Honored Contributor III
875 Views

jimdempseyatthecove wrote:

...Note, while use of pointer will provide for persistent allocatable objects you cannot use the Fortran pointer to an array as a C pointer to the blob of data. You'd still use the cptr = c_loc(a), but then this would lose the address of the Fortran array descriptor, which would be necessary for the deallocation. For this, your C interface should add a void* as an additional argument that receives the address of the array descriptor. The Fortran routine would then need to be coded to return the reference to the pointer to the array. 

If the underlying Fortran object is a pointer target (perhaps one created by allocating a Fortran pointer), and the address of that underlying object is then passed through to C, there is no need to worry about the descriptor (which is inaccessible to a standard conforming program anyway) - the C address of the underlying object and its shape, if it is an array, is all that is required if that object needs to be deallocated at some later stage.

0 Kudos
Reply