Sun Studio 12: Fortran Programming Guide

11.3 Passing Data Arguments by Reference

The standard method for passing data between Fortran routines and C procedures is by reference. To a C procedure, a Fortran subroutine or function call looks like a procedure call with all arguments represented by pointers. The only peculiarity is the way Fortran handles character strings and functions as arguments and as the return value from a CHARACTER*n function.

11.3.1 Simple Data Types

For simple data types (not COMPLEX or CHARACTER strings), define or pass each associated argument in the C routine as a pointer:

Table 11–3 Passing Simple Data Types

Fortran calls C  

C calls Fortran  


integer i
real r
external CSim
 i = 100
 call CSim(i,r)
...

----------------------------

void csim_(int *i, float *r)
{
  *r = *i;
}

int i=100;
float r;
extern void fsim_(int *i, float *r);
fsim_(&i, &r);
...

------------------------------

subroutine FSim(i,r)
  integer i
  real r
  r = i
  return
end 

11.3.2 COMPLEX Data

Pass a Fortran COMPLEX data item as a pointer to a C struct of two float or two double data types:

Table 11–4 Passing COMPLEX Data Types

Fortran calls C  

C calls Fortran  


complex w
double complex z
external CCmplx
call CCmplx(w,z)
...

------------------------------

struct cpx {float r, i;};
struct dpx {double r,i;};
void ccmplx_(
   struct cpx *w,
   struct dpx *z)
{
    w -> r = 32.;
    w -> i = .007;
    z -> r = 66.67;
    z -> i = 94.1;
} 

struct cpx {float r, i;};
struct cpx d1;
struct cpx *w = &d1
struct dpx {double r, i;};
struct dpx d2;
struct dpx *z = &d2
fcmplx_( w, z );
...

---------------

subroutine FCmplx( w, z )
  complex w
  double complex z
  w = (32., .007)
  z = (66.67, 94.1)
  return
end 

In 64-bit environments, COMPLEX values are returned in registers.

11.3.3 Character Strings

Passing strings between C and Fortran routines is not recommended because there is no standard interface. However, note the following:

A Fortran call with a character string argument is shown in the next example with its C equivalent:

Table 11–5 Passing a CHARACTER String

Fortran call:  

C equivalent:  


CHARACTER*7 S
INTEGER B(3)
...
CALL CSTRNG( S, B(2) )
... 

char s[7];
int b[3];
...
cstrng_( s, &b[1], 7L );
... 

If the length of the string is not needed in the called routine, the extra arguments may be ignored. However, note that Fortran does not automatically terminate strings with the explicit null character that C expects. This must be added by the calling program.

The call for a character array looks identical to the call for a single character variable. The starting address of the array is passed, and the length that it uses is the length of a single element in the array.

11.3.4 One-Dimensional Arrays

Array subscripts in C start with 0.

Table 11–6 Passing a One-Dimensional Array

Fortran calls C  

C calls Fortran  


integer i, Sum
integer a(9)
external FixVec
...
call FixVec ( a, Sum )
...

------------------------------

void fixvec_ (
int v[9], int *sum )
{
   int i;
   *sum = 0;
   for ( i = 0; i <= 8; i++ )
      *sum = *sum + v[i];
} 

extern void vecref_
       ( int[], int * );
...
int i, sum;
int v[9] = ...
vecref_( v, &sum );
...

------------------------------

subroutine VecRef( v, total)
  integer i, total, v(9)
  total = 0
  do i = 1,9
    total = total + v(i)
  end do
  ... 

11.3.5 Two-Dimensional Arrays

Rows and columns between C and Fortran are switched.

Table 11–7 Passing a Two-Dimensional Array

Fortran calls C  

C calls Fortran  


REAL Q(10,20)
...
Q(3,5) = 1.0
CALL FIXQ(Q)
...

------------------------------

void fixq_( float a[20][10] )
{
   ...
   a[5][3] = a[5][3] + 1.;
   ...
}

extern void
     qref_( int[][10], int *);
...
 int m[20][10] = ... ;
 int sum;
...
 qref_( m, &sum );
...

------------------------------

SUBROUTINE QREF(A,TOTAL)
  INTEGER A(10,20), TOTAL
  DO I = 1,10
    DO J = 1,20
      TOTAL = TOTAL + A(I,J)
    END DO
  END DO
  ...

11.3.6 Structures

C and Fortran 95 derived types can be passed to each other’s routines as long as the corresponding elements are compatible.( f95 accepts legacy STRUCTURE statements.)

Table 11–8 Passing Legacy FORTRAN 77 STRUCTURE Records

Fortran calls C  

C calls Fortran  


STRUCTURE /POINT/
REAL X, Y, Z
END STRUCTURE
RECORD /POINT/ BASE
EXTERNAL FLIP
...
CALL FLIP( BASE )
...

------------------------------

struct point {
    float x,y,z;
};
void flip_( struct point *v )
{
    float t;
    t = v -> x;
    v -> x = v -> y;
    v -> y = t;
    v -> z = -2.*(v -> z);
}

struct point {
    float x,y,z;
};
void fflip_ ( struct point *) ;
...
struct point d;
struct point *ptx = &d;
...
fflip_ (ptx);
...

------------------------------

SUBROUTINE FFLIP(P)
  STRUCTURE /POINT/
    REAL X,Y,Z
  END STRUCTURE
  RECORD /POINT/ P
  REAL T
  T = P.X
  P.X = P.Y
  P.Y = T
  P.Z = -2.*P.Z
  ...

Note that Fortran 77 (VAX) structures always have the same alignment as C structures on all platforms. However, the alignment changes between platforms.

Table 11–9 Passing Fortran 95 Derived Types

Fortran 95 calls C  

C calls Fortran 95  


TYPE point
  SEQUENCE
  REAL :: x, y, z
END TYPE point
TYPE (point) base
EXTERNAL flip
...
CALL flip( base)
...

------------------------------

struct point {
    float x,y,z;
};
void flip_( struct point *v )
{<
    float t;
    t = v -> x;
    v -> x = v -> y;
    v -> y = t;
    v -> z = -2.*(v -> z);
}

struct point {
  float x,y,z;
};
extern void fflip_ (
     struct point *) ;
...
struct point d;
struct point *ptx = &d;
...
fflip_ (ptx);
...

------------------------------

SUBROUTINE FFLIP( P )
    TYPE POINT
     SEQUENCE
     REAL :: X, Y, Z
    END TYPE POINT
    TYPE (POINT) P
    REAL :: T
    T = P%X
    P%X = P%Y
    P%Y = T
    P%Z = -2.*P%Z
    ...

Note that the Fortran 95 standard requires the SEQUENCE statement in the definition of the derived type to insure that storage sequence order be preserved by the compiler.

The components of a numeric sequence type are aligned on word (4-byte) boundaries on all platforms by default. This matches the alignment of C structures on x86 platforms, but differs from the alignment of C structures on SPARC platforms. Use the -aligncommon option to change the alignment of numeric sequence types to match C structures. Use -aligncommon=8 to match 32–bit SPARC C structures, -aligncommon=16 to match 64–bit SPARC.

Derived types not explicitly declared with SEQUENCE have the same alignment as C structures on SPARC platforms, but differ on x86 platforms. This alignment cannot be changed with a compiler option.

11.3.7 Pointers

A FORTRAN 77 (Cray) pointer can be passed to a C routine as a pointer to a pointer because the Fortran routine passes arguments by reference.

Table 11–10 Passing a FORTRAN 77 (Cray) POINTER

Fortran calls C  

C calls Fortran  


REAL X
POINTER (P2X, X)
EXTERNAL PASS
P2X = MALLOC(4)
X = 0.
CALL PASS(P2X)
...

------------------------------

void pass_(p)
> float **p;
{
  **p = 100.1;
}

extern void fpass_( float** );
...
float *p2x;
...
 fpass_(&p2x) ;
...

------------------------------

SUBROUTINE FPASS (P2X)
  REAL X
  POINTER (P2X, X)
  X = 0.
  ...

C pointers are compatible with Fortran 95 scalar pointers, but not array pointers.

Fortran 95 calls C with a scalar pointer  

Fortran 95 routine:


INTERFACE
  SUBROUTINE PASS(P)
   REAL, POINTER :: P
  END SUBROUTINE
END INTERFACE

 REAL, POINTER :: P2X
 ALLOCATE (P2X)
 P2X = 0
 CALL PASS(P2X)
 PRINT*, P2X
END

C routine:


void pass_(p);
float **p;
{
  **p = 100.1;
}

 

The major difference between Cray and Fortran 95 pointers is that the target of a Cray pointer is always named. In many contexts, declaring a Fortran 95 pointer automatically identifies its target. Also, an explicit INTERFACE block is required for the called C routine.

To pass a Fortran 95 pointer to an array or array section requires a specific INTERFACE block, as in this example:


Fortran 95 routine:
INTERFACE
  SUBROUTINE S(P)
    integer P(*)
  END SUBROUTINE S
END INTERFACE
integer, target:: A(0:9)
integer, pointer :: P(:)
P => A(0:9:2) !! pointer selects every other element of A
call S(P)
...

C routine:
void s_(int p[])
{
   /* change middle element */
   p[2] = 444;
}

Note that since the C routine S is not a Fortran 95 routine, you cannot define it to be assumed shape (integer P(:)) in the interface block. If the C routine needs to know the actual size of the array it must be passed as an argument to the C routine.

Again, keep in mind that subscripting between C and Fortran differs in that C arrays start at subscript 0.