Lahey/Fujitsu Fortran  

Type Correspondence

When passing arguments to a subprogram, it is necessary that they match the list of formal parameters on the entry point. The following table shows what various Fortran variable types correspond to in C.

 

Variable Type Equivalents

Fortran Type

Kind No.

C Type

INTEGER

1

char

INTEGER

2

int

INTEGER

4

long

REAL

4

float

REAL

8

double

COMPLEX

4

struct{float xr, xi;}

COMPLEX

8

struct{double xr, xi;}

LOGICAL

1

char

LOGICAL

4

long

CHARACTER

1

(none)

The C language allows unsigned integers of various lengths. There is no direct analog of this in Fortran, however the unsigned integers that can be returned from C can be stored and slightly manipulated in Fortran. Fortran cannot perform arithmetic on unsigned integers, however this is often unnecessary: one of the most common uses for unsigned integers is as handles for files, windows, and other objects.

Handles are received, copied, and passed into other routines, but are never subjected to computation. It is therefore possible to treat a handle as simply an INTEGER of the appropriate length and there will be no problem. If it is necessary to display the value of a handle it can be done in hexadecimal (Z) format, with no loss of information.

Example 1 --- A Simple Subroutine

First, let us look at the simplest example of a Fortran program calling a C subroutine. The following main program defines two integers, I and J, and then calls SUB to add them and return the sum.

      PROGRAM MAIN
      integer :: i,j,k
      i = 12
      j = 43
      k = 0
      print *, 'Before: i,j,k=',i,j,k
      call sub(i,j,k)
      print *, 'After:  i,j,k=',i,j,k
      stop
      end
      

This is the subroutine that performs the addition:

void sub_(i,j,k)
int *i, *j, *k;
{
  *k = *i + *j;
  return;
}

In C, a subroutine is a function of type "void." As we noted earlier, the name of the subroutine must be in lower case letters, with a trailing underscore. Since Fortran normally passes arguments by reference, the C subroutine must receive them as pointers (hence the "*" in front of the variable names). The type INTEGER variables in Fortran are treated as type "int" in C.

Example 2 --- Passing Real Arguments

The situation is the same when floating point arguments are passed. In this example, three default REAL(KIND=4) arguments are sent to a C subroutine, where they are manipulated.

      PROGRAM FLTMAIN
      x = 2.17
      y = 5.6
      z = 0.0
      print *,' x,y,z=',x,y,z
      call cmult(x,y,z)
      print *,' x,y,z=',x,y,z
      stop
      end
      

This is the C subroutine, where the REAL(KIND=4) variables are received as pointers to variables of type "float." If the arguments were REAL(KIND=8), then the C side would expect them as type "double."

void cmult_(x,y,z)
float *x, *y, *z;
{
  *z = *x * *y + 2.0;
  return;
}

Example 3 --- Passing CHARACTER Arguments

Passing type CHARACTER variables poses a higher level of complexity. Consider the following main program, which assigns a literal string to A, and then calls CHRCOPY to duplicate A into B.

      PROGRAM CHRMAIN
      character*20 a, b
      a = 'This is a message'
      b = ' '
      print *, 'a=',a
      print *, 'b=',b
      call chrcopy(a,b)
      print *, 'a=',a
      print *, 'b=',b
      stop
      end
      

When LF95 passes type CHARACTER arguments to a subroutine, it actually sends both the starting address of the string, plus the length (which is passed by value). The lengths of any CHARACTER arguments are treated as hidden arguments to the right of the normal argument list.

Thus, in the following C subroutine, the argument list consists of four items, even thought we could see only two in the Fortran CALL statement. The first two arguments are the character strings, passed by reference so that they appear here as pointers to variables of type "char." (Type "char" in C is not a true string variable, but is rather a one-byte integer.)

The third and fourth arguments are the lengths of A and B, passed by value. We can tell that they are being passed by value here because they are not prefixed by asterisks, but just appear as plain variables.

#include <string.h>
void chrcopy_(a,b,na,nb)
char *a, *b;
int na, nb;
{
  int nmin;
  nmin = na > nb ? nb : na;
  strncpy(b,a,nmin);
  return;
}

The subroutine first compares the lengths of the two CHARACTER variables, and then selects the minimum (in case they are different). That becomes the number of characters to copy from A to B, and the C library routine "strncpy" is used.

Example 4 --- Passing ASCIIZ Arguments

In early Fortran compilers, character strings were stored as arrays of numeric storage locations, packed several characters to each word and then terminated by a word or partial word of zero. Because different types of computer have different word lengths, this "Hollerith" scheme often led to seriously non-transportable code. Some computers stored four characters per word, while others stored five, six, eight, or ten characters per word and so many routines that performed input or output required drastic reworking when moved from one brand of computer to another.

When the Basic language was released in the 1970s, it introduced the notion of a special "string" data type that was independent of the hardware design. This was such a good idea that it was copied into the 1977 Fortran standard as CHARACTER variables.

Unfortunately, at the same time that Fortran was copying from Basic, C was copying from Fortran and so currently C compilers still expect character strings to be stored as an array of numeric storage locations (usually bytes), terminated by a null. In some cases, you may find it preferable to pass CHARACTER variables to C by appending a null, so that it looks like the legacy method expected by the C language. In order to do this, you would change

CALL CSUB(...,ASTR,...)

into

CALL CSUB(...,ASTR//CHAR(0),...)

where ASTR is a CHARACTER variable. In this case, however, the Fortran compiler will make a copy of ASTR with the null attached, and pass that. This means that the subroutine will not be able to modify the original string since ASTR//CHAR(0) is an expression rather than a variable, but that may well be desirable.

If you want to allow the subroutine to modify the original string, then you should add the null into the CHARACTER variable, as shown in the following example.

      PROGRAM CHRMAIN
      character*20 a, b
      a = 'Original text'//char(0)
      b = ' '
      print *, 'a=',a
      print *, 'b=',b
      call chrcaps(a,b)
      print *, 'a=',a
      print *, 'b=',b
      stop
      end
      

Here is a C subroutine that returns B as a capitalized version of A, as required by the main program.

void chrcaps_(a,b,na,nb)
char *a, *b;
int na, nb;
{
  char *i, *j;
  for (i = a, j = b; *i != 0; i++, j++) {
    *j = *i;
    if (*j >= 97 && *j <= 122) *j -= 32;
    }
  return;
}

In this case, the copying operation is halted by the appearance of a null (the "*i != 0" clause in the "for" statement). Local pointer variables *i and *j are used instead of the ones that were supplied by the caller.

Example 5 --- Accessing COMMON Blocks

When LF95 processes COMMON blocks, it modifies them in the same way as it does entry points. That is to say that a block named /SAND/ will invisibly become a global object named "_sand_" and this alteration must be dealt with when performing inter-language calling. The secret name of blank COMMON is "__BLNK__", with two underscores in front and behind.

Here is an example of a Fortran main program that supplies values to some variables that are in COMMON blocks, one blank and one named.

      PROGRAM CMN_MAIN
      integer :: i
      real :: x,y,z
      common /zulu/ x, y
      common z
      i = 12
      x = 4.5
      y = 0.0
      z = 8.1
      print *, 'Before: i,x,y,z=',i,x,y,z
      call ccmn(i)
      print *, 'After: i,x,y,z=',i,x,y,z
      stop
      end
      

That program calls the following C subroutine:

extern struct
{
  float x, y;
} zulu_;
 
extern struct
{
  float z;
} _BLNK__;
 
void ccmn_(i)
int *i;
{
  zulu_.y = zulu_.x + (float)(*i);
  _BLNK__.z += zulu_.x;
  return;
}

In order to access the COMMON blocks from C, we must define a pair of structures, and declare them outside of the function body so that they acquire the global attribute and can connect to the COMMON blocks that the Fortran compiler is going to set up.

Since C prepends an underscore to the global names, the named common /ZULU/, which is called "_zulu_" in the object modules, must be called "zulu_" (no leading underscore) in the C code. Likewise, the blank COMMON, called "__BLNK__" in the object code, is called "_BLNK__" (only one leading underscore) in C.

Example 6 --- Functions

Calling a function that is written in C, one that returns a value (as opposed to a subroutine), is fairly simple as long as you make sure that the type of the function in C matches what Fortran expects to receive.

Here is an example of a Fortran main program that calls several C functions, each of a different type. The argument lists are the same for all the functions: two default integers, but the return value differs.

      PROGRAM MAIN
      integer :: i,j
      integer(kind=1) :: k1, i1add
      integer(kind=2) :: k2, i2add
      integer(kind=4) :: k4, i4add
      real(kind=4) :: r4, r4add
      real(kind=4) :: r8, r8add
      external :: i1add, i2add, i4add, r4add, r8add
!
      i = 12
      j = 43
      k1 = 0; k2 = 0; k4 = 0
      print *, 'Before: i,j=',i,j
      k1 = i1add(i,j)
      k2 = i2add(i,j)
      k4 = i4add(i,j)
      print *, 'After:  k1,k2,k4=',k1,k2,k4
      r4 = r4add(i,j)
      r8 = r8add(i,j)
      print *, 'r4,r8=',r4,r8
!
      stop
      end
      

These are the C functions called by the Fortran main. Note that the type of variable for a function to return is specified in the opening statement, in place of the "void" that was used in the earlier subroutines.

char i1add_(i,j)
int *i, *j;
{
  char k;
  k = *i + *j;
  return(k);
}
 
short i2add_(i,j)
int *i, *j;
{
  short k;
  k = *i - *j;
  return(k);
}
 
long i4add_(i,j)
int *i, *j;
{
  long k;
  k = *i * *j;
  return(k);
}
 
float r4add_(i,j)
int *i, *j;
{
  float r;
  r = (float)(*i) + (float)(*j);
  return(r);
}
 
double r8add_(i,j)
int *i, *j;
{
  double d;
  d = (double)(*i) / (double)(*j);
  return(d);
}