next up previous 252
Next: Function Values
Up: VAX/VMS
Previous: Data Types


Arguments

To understand how to pass arguments between VAX FORTRAN and VAX C programs, it is necessary to understand the possible methods that VMS can use for passing arguments and how each language makes use of them. VMS defines a procedure calling standard that is used by all compilers written by DEC for the VMS operating system. This is described in the ``Introduction to the VMS Run-Time Library'' manual with additional information in the ``Introduction to VMS System Services'' manual. If you have a third party compiler that does not conform to this standard then you will not be able to mix the object code that it produces with that from DEC compilers. There are three ways that an actual argument may be passed to a subroutine. What is actually passed as an argument should always be a longword. It is the interpretation of that longword that is where the differences arise. Note the word should in the last but one sentence. VAX C will occasionally generate an argument that is longer than one longword. This is a violation of the VAX procedure calling standard. It causes no problems for pure VAX C programs, but is a potential source of problems for mixed language programs.

VAX FORTRAN passes all data types other than CHARACTER by reference, i.e. the address of the variable or array is put in the argument list. CHARACTER variables are passed by descriptor. The descriptor contains the type and class of descriptor, the length of the string and the address where the characters are actually stored.

VAX C uses call by value to pass all variables, constants (except string constants), expressions, array elements, structures and unions that are actual arguments of functions. It uses call by reference to pass whole arrays, string constants and functions. VAX C never uses call by descriptor as a default method of passing arguments.

To pass a VAX C variable of type double by value requires the use of two longwords in the argument list and so is a violation of the VAX procedure calling standard. The passing of a VAX C structure that is bigger that one longword is a similar violation. It is always better to pass C structures by reference, although this should not be a problem in practice since in the case of a pure VAX C program, everything is handled consistently and in the case of a mixture of FORTRAN and C, you would not normally pass variables by value anyway.

In VAX FORTRAN, the default argument passing mechanism can be overridden by use of the %VAL, %REF and %DESCR functions. These functions are not portable and should be avoided whenever possible. The only exception is that %VAL is used in Starlink software for passing pointer variables. In VAX C there is no similar way of ``cheating'' as there is in VAX FORTRAN; however, this is not necessary as the language allows more flexibility itself. For example, if you wish to pass a variable named x by reference rather than by value, you simply put &x as the actual argument instead of x. To pass something by descriptor, you need to construct the appropriate structure and pass the address of that. See the DEC manual ``Guide to VAX C'' for further details.

Since C provides more flexibility in the mechanism of passing arguments than does FORTRAN, it is C that ought to shoulder the burden of handling the different mechanisms. All numeric variables and constants, array elements, whole arrays and function names should be passed into and out of C functions by reference. Numeric expressions will be passed from VAX FORTRAN to VAX C by reference and so the corresponding dummy argument in the C function should be declared to be of type ``pointer to type''. When C has a constant or an expression as an actual argument in a function call, it can only pass it by value. VAX FORTRAN cannot cope with this and so in a VAX C program, all expressions should be assigned to variables before being passed to a FORTRAN routine.

Here are some examples to illustrate these points.

Example - Passing arguments from VAX FORTRAN to VAX C.
FORTRAN program:
      PROGRAM FORT1
      INTEGER A
      REAL B
      A = 1
      B = 2.0
      CALL C1( A, B )
      END
C function:
      void c1( int *a, float *b )
      {
         int x;
         float y;

         x = *a;    /* x is now equal to 1 */
         y = *b;    /* y is now equal to 2.0 */

         printf( "x = %d\n", x );
         printf( "y = %f\n", y );
      }

In this first example, a FORTRAN program passes an INTEGER and REAL variable to a C function. The values of these arguments are then assigned to two local variables. They could just as well have been used directly in the function by referring to the variables *a and *b instead of assigning their values to the local variables x and y. Since the VAX FORTRAN program passes the actual arguments by reference, the dummy arguments used in the declaration of the VAX C function should be a pointer to the variable that is being passed.

Now an example of calling a VAX FORTRAN subroutine from VAX C.

Example - Passing arguments from VAX C to VAX FORTRAN.
C main program:
      main()
      {
        int  i = 2;            /* Declare i and initialize it.  */
        void fort2( int *i );  /* Declare function fort2. */

        fort2( &i );           /* Call fort2.  */
      }
FORTRAN subroutine:
      SUBROUTINE FORT2( I )
      INTEGER I

      PRINT *,I

      END

The VAX C main function declares and initializes a variable, i, and declares a function fort2. It calls fort2, passing the address of the variable i rather than its value, as this is what the VAX FORTRAN subroutine will be expecting.

As we have seen, the case of scalar numeric arguments is fairly straightforward. However, the passing of CHARACTER variables between VAX FORTRAN and VAX C is more complicated. VAX FORTRAN passes CHARACTER variables by descriptor and VAX C must handle these descriptors. Furthermore, there is the point that FORTRAN deals with fixed-length, blank-padded strings, whereas C deals with variable-length, null-terminated strings. It is also worth noting that VAX/VMS machines handle CHARACTER arguments in a manner which is different from the usual Unix way. The simplest possible example of a CHARACTER argument is given here in all of its gory detail. You will be pleased to discover that this example is purely for illustration. The important point is that it is different from the Sun example and, anyway, the F77 macros hide all of these differences from the programmer, thereby making the code portable.

Example - Passing character arguments from VAX FORTRAN to VAX C.
FORTRAN program:
      PROGRAM FORT3
      CHARACTER STR*20

      CALL C3( STR )
      PRINT *,STR

      END
C function:
      #include <descrip.h>                    /* VMS Descriptors */
      #include <stdio.h>                      /* Standard I/O functions */

      void c3( struct dsc$descriptor_s  *fortchar )
      {
         int  i;                              /* A loop counter */
         char  *string = "This is a string";  /* A string to be printed */

      /* Copy the string to the function argument */
         strncpy( fortchar->dsc$a_pointer, string, fortchar->dsc$w_length );

      /* Pad the character argument with trailing blanks */
         for( i = strlen( string ) ; i < fortchar->dsc$w_length ; i++ )
            fortchar->dsc$a_pointer[i] = ' ';
      }

The second variable declaration in the C subprogram declares a local variable to be a string and initializes it. This string is then copied to the storage area that the subprogram argument points to, taking care not to copy more characters than the argument has room for. Finally any remaining space in the argument is filled with blanks, the null character being overwritten. You should always fill any trailing space with blanks in this way. What should definitely not be done is to modify the descriptor to indicate the number of non blank characters that it now holds. The VAX FORTRAN compiler will not expect this to happen and it is likely to cause run-time errors. See the DEC manual ``Guide to VAX C'' for more details of handling descriptors in VAX C.

If an actual argument in a VAX FORTRAN routine is an array of characters, rather than just a single character variable, the descriptor that describes the data is different. It is defined by the macro dsc$descriptor_a instead of dsc$descriptor_s. This contains extra information about the number of dimensions and their bounds; however, this can generally be ignored since the first part of the dsc$descriptor_a descriptor is the same as the dsc$descriptor_s descriptor. This extra information can be unpacked from the descriptor, however, to do so would lead to non-portable code. It is generally better to use the address of the array that is passed in the descriptor and to pass any array dimensions as separate arguments. The C subroutine then has all of the information that it requires and can handle the data as an array or by using pointers, as the programmer sees fit. See example [*] for an illustration of this.



next up previous 252
Next: Function Values
Up: VAX/VMS
Previous: Data Types

CNF and F77 Mixed Language Programming -- FORTRAN and C
Starlink User Note 209
P.M. Allan
A.J. Chipperfield
R.F. Warren-Smith
19 January 2000
E-mail:ussc@star.rl.ac.uk