United States

Compaq C

# Compaq C
User's Guide for OpenVMS Systems

Previous | Contents | Index

## 3.2.3 Passing Arguments by Descriptor

A descriptor is a structure that describes the data type, size, and address of a data structure. According to the OpenVMS Calling Standard, you must pass a descriptor by placing its address in the argument list. To pass an argument by descriptor from a Compaq C program, perform the following steps:

1. Write a structure declaration that models the required descriptor. This involves including the `<descrip.h>` header file to define `struct` tags for all the forms of descriptors.
2. Assign appropriate values to the structure members.
3. Use the structure name, with an ampersand operator (&) in the function reference, to put the structure's address in the argument list.

Compaq C never passes arguments by descriptor by default; you must take explicit action to pass an argument by descriptor. Also, if you write structure or union names in a function's argument list without the ampersand operator, the structure or union is passed by immediate value to the called function. You pass arguments by descriptor only when the called function is written in another language and explicitly requires this mechanism.

Note The passing of structures as immediate values can be a violation of the OpenVMS calling standard if the entire structure is larger than one longword of memory. This type of argument passing is an allowed exception for Compaq C.

There are several classes of descriptor. Each class requires that certain bits be set in the first longword of the descriptor. For more information about the descriptors and their formats, see the OpenVMS Programming Interfaces: Calling a System Routine. You can model descriptors in Compaq C as follows:

    struct  dsc$descriptor
       {
          unsigned  short  dsc$w_length; /*  Length of data         */
          char  dsc$b_dtype              /*  Data type code         */
          char  dsc$b_class              /*  Descriptor class code  */
          char  *dsc$a_pointer           /*  Address of first byte  */
       };

In this model, the variable `dsc$w_length` is a 16-bit word containing the length of the entire data; the unit (for example, bit or byte) in which the length is measured depends on the descriptor class. The member `dsc$b_dtype` is a byte containing a numeric code; the code denotes the data type of the data. The class member `dsc$b_class` is another byte code giving the descriptor class. Table 3-5 shows the valid class codes.

Table 3-5 Valid Class Codes
Class Code | Symbolic Name | Descriptor Class
--- | --- | ---
1 | DSC$K_CLASS_S | Scalar, string
2 | DSC$K_CLASS_D | Dynamic string descriptor
3 | --- | Reserved by DIGITAL
4 | DSC$K_CLASS_A | Array
5 | DSC$K_CLASS_P | Procedure
6 | DSC$K_CLASS_PI | Procedure incarnation
7 | DSC$K_CLASS_J | Reserved by DIGITAL
8 | DSK$K_CLASS_JI | This is obsolete
9 | DSC$K_CLASS_SD | Decimal scalar string
10 | DSC$K_CLASS_NCA | Noncontiguous array
11 | DSC$K_CLASS_VS | Varying string
12 | DSC$K_CLASS_VSA | Varying string array
13 | DSC$K_CLASS_UBS | Unaligned bit string
14 | DSC$K_CLASS_UBA | Unaligned bit array
15 | DSC$K_CLASS_SB | String with bounds descriptor
16 | DSC$K_CLASS_UBSB | Unaligned bit string with bounds descriptor
17-190 | --- | Reserved by DIGITAL
191 | DSC$K_CLASS_BFA | Basic file array
192-255 | --- | Reserved for customer applications

The atomic data types shown in Table 3-6 are supported by Compaq C; all others are not directly supported by the language. See the OpenVMS Programming Interfaces: Calling a System Routine manual for a complete list of atomic class codes.

Table 3-6 Atomic Data Types
Class Code | Symbolic Name | Descriptor Class
--- | --- | ---
2 | DSC$K_DTYPE_BU | Byte (unsigned)
3 | DSC$K_DTYPE_WU | Word (unsigned)
4 | DSC$K_DTYPE_LU | Longword (unsigned)
6 | DSC$K_DTYPE_B | Byte integer (signed)
7 | DSC$K_DTYPE_W | Word integer (signed)
8 | DSC$K_DTYPE_L | Longword integer (signed)
10 | DSC$K_DTYPE_F | F_floating
11 | DSC$K_DTYPE_D | D_floating
27 | DSC$K_DTYPE_G | G_floating
52 | DSC$K_DTYPE_FS | IEEE S_floating
53 | DSC$K_DTYPE_FT | IEEE T_floating

The last member of the structure model, `dsc$a_pointer` , points to the first byte of the data.

To pass an argument by descriptor, you define and assign values to the data following normal C programming practices. You must define a `dsc$descriptor` structure and assign the data's address to the `dsc$a_pointer` member. You must also assign appropriate values to the members `dsc$w_length` , `dsc$b_dtype` , and `dsc$b_class` . For the specific requirements of each descriptor class, see the OpenVMS Programming Interfaces: Calling a System Routine manual.

For example, the Set Process Name (SYS$SETPRN) system service, which enables a process to establish or change its process name, accepts a process name as a fixed-length character string passed by descriptor. The character string can have from 1 to 15 characters. The system service returns status values that are represented by the symbolic constants shown in Table 3-7.

Table 3-7 Status Values of SYS$SETPRN
Returned | Status | Description
--- | --- | ---
SS$_NORMAL | Success | Normal completion
SS$_ACCVIO | Failure | Inaccessible descriptor
SS$_DUPLNAM | Failure | Duplicate process name
SS$_IVLOGNAM | Failure | Invalid length

Example 3-3 shows a call to this system service from a Compaq C program.

Example 3-3 Passing Arguments by Descriptor

    /*  This program shows a call to system service SYS$SETPRN.    */

    #include <ssdef.h>
    #include <stdio.h>
                                       /*  Define structures for   *
                                        *   descriptors            */
    #include <descrip.h>

    int SYS$SETPRN();

    int main(void)
    {
       int  ret;                       /*  Define return status of *
                                        *   SYS$SETPRN             */

                                       /*  Name the descriptor     */
       struct  dsc$descriptor_s  name_desc;

       char  *name =  "NEWPROC";       /*  Define new process name */
          .
          .
          .
                                       /*  Length of name WITHOUT  *
                                        *   null terminator        */
       name_desc.dsc$w_length = strlen(name);

                                       /*  Put address of          *
                                        *   shortened string in    *
                                        *   descriptor             */
       name_desc.dsc$a_pointer =  name;

                                       /*  String descriptor class */
       name_desc.dsc$b_class =  DSC$K_CLASS_S;

                                       /*  Data type: ASCII string */
       name_desc.dsc$b_dtype =  DSC$K_DTYPE_T;
          .
          .
          .
       ret =  SYS$SETPRN(&name_desc);

       if (ret != SS$_NORMAL)          /*  Test return status      */
          fprintf(stderr, "Failed to set process name\n"),
          exit(ret);
          .
          .
          .
    }

In Example 3-3, the call to SYS$SETPRN must use the ampersand operator; otherwise, `name_desc` , rather than its address, is passed.

Although this example explicitly sets individual fields in its `name_desc` string descriptor, in practice, the run-time initialization of compile-time constant string descriptors is not performed in this manner. Instead, the fields of compile-time constant descriptors are usually initialized with initialized structures of storage class `static` .

For the purpose of string descriptor initialization, Compaq C provides a simple preprocessor macro in the `<descrip.h>` header file. This macro is named $DESCRIPTOR. It takes two arguments, which it uses in a standard Compaq C structure declaration. The first argument is an identifier specifying the name of the descriptor to be declared and initialized. The second argument is a pointer to the data byte to be used as the value of the descriptor. Since a character-string constant is interpreted as an initialized pointer to `char` , you may specify the second argument as a simple string constant. You may use the $DESCRIPTOR macro in any context where a declaration may be used. The scope of the declared string descriptor identifier name is identical to the scope of a simple `struct` definition as expanded by the macro.

Example 3_4 shows a variant of the program in Example 3-3. Here, the $DESCRIPTOR macro is used to create a compile-time string descriptor and to pass it to the SYS$SETPRN system service routine. In Example 3-4, the program returns the status value returned by SYS$SETPRN to DCL for interpretation.

Example 3-4 Passing Compile-Time String Descriptors

    /*  This program returns the status value returned by          *
     *  SYS$SETPRN.                                                */

    #include  <descrip.h>              /*  Define $DESCRIPTOR      *
                                        *   macro                  */
    int  SYS$SETPRN();

    main(void)
    {
                                       /*  Initialize structure    *
                                        *   name_desc as string    *
                                        *   descriptor             */
       static  $DESCRIPTOR(name_desc,"NEWPROC");

       return  SYS$SETPRN(&name_desc);
    }

The $DESCRIPTOR macro is used in further examples in this chapter.

## 3.2.4 Compaq C Default Parameter-Passing Mechanisms

There are default parameter-passing mechanisms established for every data type you can use with Compaq C. Table 3-8 lists the Compaq C data types you can use with each parameter-passing mechanism. Asterisks appear next to the default parameter-passing mechanism for that particular data type.

Table 3-8 Valid Parameter-Passing Mechanisms in Compaq C
Data Type | By Reference | By Descriptor | By Value
--- | --- | --- | ---
Variables | Yes | Yes | Yes*
Constants | Yes (VAX C mode only) | Yes | Yes*
Expressions | No | No | Yes*
Array elements | Yes | Yes | Yes*
Entire array | Yes* | Yes | No
String constants | Yes* | Yes | No
Structures and unions | Yes | Yes | Yes*
Functions | Yes* | Yes | No

You must use the appropriate parameter-passing mechanisms whenever you call a routine written in some other OpenVMS language or some prewritten system routine.

# 3.3 Interlanguage Calling

In Compaq C, you can call external routines written in other languages or Compaq C routines from routines written in other languages as either functions or subroutines. When you call an external routine as a function, a single value is returned. When you call an external routine as a subroutine (a `void` function), any values are returned in the argument list.

By default, Compaq C passes all arguments by immediate value with the exception of arrays and functions; these are passed by reference. Table 3-9 lists the default passing mechanisms for other OpenVMS languages.

Table 3-9 Default Passing Mechanisms
Language | Arrays | Numeric Data | Character Data
--- | --- | --- | ---
MACRO | No default | No default | No default
Pascal | Reference | Reference | Descriptor
BASIC | Descriptor | Reference | Descriptor
COBOL | N/A | Reference | Reference
FORTRAN | Reference | Reference | Descriptor

The following sections describe the methods involved in using Compaq C with routines written in other OpenVMS languages.

## 3.3.1 Calling Compaq FORTRAN

When calling Compaq FORTRAN from Compaq C or vice versa, note these considerations. Compaq FORTRAN argument lists and argument descriptors are usually allocated statically. When it is possible, and to optimize space and time, the Compaq FORTRAN compiler pools the argument lists and initializes them at compile time. Sometimes several calls may use the same argument list.

In Compaq C, you often use arguments as local variables, and modify them at will. If a Compaq C routine that modifies an argument is called from a Compaq FORTRAN routine, unintended and incorrect side effects may occur.

The following example shows a Compaq C routine that is invalid when called from Compaq FORTRAN:

    void f(int *x)      /* x is a FORTRAN INTEGER passed by reference */

    {
        /* The next assignment is OK.  It is permitted to modify what a
         * FORTRAN argument list entry points to. */
        *x = 0;                /* ok */

        /* The next assignment is invalid.  It is not permitted to modify
         * a FORTRAN argument list entry itself. */
        x = x + 1;                /* Invalid */
    }

Another problem is the semantic mismatch between strings in C and strings in Compaq FORTRAN. Strings in C vary in length and end in a null character. Strings in Compaq FORTRAN do not end in a null character and are padded with spaces to some fixed length. In general, this mismatch means that strings may not be passed between Compaq C and Compaq FORTRAN unless you do additional work. You may make a Compaq FORTRAN routine add a null character to a CHARACTER string before calling a Compaq C function. You may also write code that explicitly gets the length of a Compaq FORTRAN string from its descriptor and carefully pads the string with spaces after modifying it. An example later in this section shows a C function that carefully produces a proper string for Compaq FORTRAN.

Example 3-5 shows a Compaq C function calling a Compaq FORTRAN subprogram with a variety of data types. For most scalar types, Compaq FORTRAN expects arguments to be passed by reference but character data is passed by descriptor.

Example 3-5 Compaq C Function Calling a Compaq FORTRAN Subprogram

    /*
     *  Beginning of Compaq C function:
     */

    #include <stdio.h>
    #include <descrip.h>                 /* Get layout of descriptors */

    extern int fort();                   /* Declare FORTRAN function */

    main(void)
    {
        int i = 508;
        float f = 649.0;
        double d = 91.50;
        struct {
            short s;
            float f;
        } s = {-2, -3.14};
        auto $DESCRIPTOR(string1, "Hello, FORTRAN");
        struct dsc$descriptor_s string2;

        /* "string1" is a FORTRAN-style string declared and initialized using the
         * $DESCRIPTOR macro.  "string2" is also a FORTRAN-style string, but we are
         * declaring and initializing it by hand. */
        string2.dsc$b_dtype = DSC$K_DTYPE_T;  /* Type is CHARACTER */
        string2.dsc$b_class = DSC$K_CLASS_S;  /* String descriptor */
        string2.dsc$w_length = 3;             /* Three characters in string */
        string2.dsc$a_pointer = "bye";        /* Pointer to string value */

        printf("FORTRAN result is %d\n", fort(&i, &f, &d, &s, &string1, &string2));
    }   /* End of Compaq C function */

    C
    C     Beginning of FORTRAN subprogram:
    C
          INTEGER FUNCTION FORT(I, F, D, S, STRING1, STRING2)
          INTEGER I
          REAL F
          DOUBLE PRECISION D
          STRUCTURE /STRUCT/
          INTEGER*2 SHORT
          REAL FLOAT
          END STRUCTURE
          RECORD /STRUCT/ S
    C     You can tell FORTRAN to use the length in the descriptor
    C     as done here for STRING1, or you can tell FORTRAN to ignore the
    C     descriptor and assume the string has a particular length as done
    C     for STRING2.  This choice is up to you.
          CHARACTER*(*) STRING1
          CHARACTER*3 STRING2

          WRITE(5, 10) I, F, D, S.SHORT, S.FLOAT, STRING1, STRING2
    10    FORMAT(1X, I3, F8.1, D10.2, I7, F10.3, 1X, A, 2X, A)
          FORT = -15
          RETURN
          END
    C     End of FORTRAN subprogram

Example 3-5 produces the following output:

    508   649.0  0.92D+02     -2    -3.140 Hello, FORTRAN  bye
    FORTRAN result is -15

Example 3-6 shows a Compaq FORTRAN subprogram calling a Compaq C function. Since the Compaq C function is called from Compaq FORTRAN as a subroutine and not as a function, the Compaq C function is declared to have a return value of `void` .

Example 3-6 Compaq FORTRAN Subprogram Calling a Compaq C Function

    C
    C     Beginning of FORTRAN subprogram:
    C
          INTEGER I
          REAL F(3)
          CHARACTER*10 STRING

    C     Since this program does not have a C main program and you want
    C     to use Compaq C RTL functions from the C subroutine, you must call
    C     VAXC$CRTL_INIT to initialize the run-time library.
          CALL VAXC$CRTL_INIT

          I = -617
          F(1) = 3.1
          F(2) = 0.04
          F(3) = 0.0016
          STRING = 'HELLO'

          CALL CSUBR(I, F, STRING)
          END
    C     End of FORTRAN subprogram

    /*
     *  Beginning of Compaq C function:
     */
    #include <stdio.h>
    #include <descrip.h>                       /* Get layout of descriptors */

    void csubr(int *i,                         /* FORTRAN integer, by reference */
        float f[3],                            /* FORTRAN array, by reference */
        struct dsc$descriptor_s *string)       /* FORTRAN character, by descriptor */
    {
        int j;

        printf("i = %d\n", *i);

        for (j = 0; j < 3; ++j)
            printf("f[%d] = %f\n", j, f[j]);

        /* Since FORTRAN character data is not null-terminated, you must use
         * a counted loop to print the string.
         */
        printf("string = \"");
        for (j = 0; j < string->dsc$w_length; ++j)
            putchar(string->dsc$a_pointer[j]);
        printf("\"\n");

    }   /*  End of Compaq C function  */

Example 3-6 produces the following output:

    i = -617
    f[0] = 3.100000
    f[1] = 0.040000
    f[2] = 0.001600
    string = "HELLO     "

Example 3-7 shows a C function that acts like a CHARACTER*(*) function in Compaq FORTRAN.

Example 3-7 Compaq C Function Emulating a Compaq FORTRAN CHARACTER*(*) Function

    C
    C       Beginning of FORTRAN program:
    C
            CHARACTER*9 STARS, C

    C       Call a C function to produce a string of three "*" left-justified
    C       in a nine-character field.
            C = STARS(3)

            WRITE(5, 10) C
    10      FORMAT(1X, '"', A, '"')
            END
    C       End of FORTRAN program

    /*
     *  Beginning of Compaq C function:
     */

    #include <descrip.h>                        /* Get layout of descriptors */

    /* Routine "stars" is equivalent to a FORTRAN function declared as
     * follows:
     *
     *        CHARACTER*(*) FUNCTION STARS(NUM)
     *        INTEGER NUM
     *
     * Note that a FORTRAN CHARACTER function has an extra entry added to
     * the argument list to represent the return value of the CHARACTER
     * function.  This entry, which appears first in the argument list,
     * is the address of a completely filled-in character descriptor.  Since
     * the C version of a FORTRAN character function explicitly uses this
     * extra argument list entry, the C version of the function is void!
     *
     * This example function returns a string that contains the specified
     * number of asterisks (or "stars").
     *
     */

    void stars(struct dsc$descriptor_s *return_value, /* FORTRAN return value */
               int *num_stars)                 /* Number of "stars" to create */
    {
        int i, limit;

        /* A FORTRAN string is truncated if it is too large for the memory area
         * allocated, and it is padded with spaces if it is too short.  Set limit
         * to the number of stars to put in the string given the size of the area
         * used to store it. */
        if (*num_stars < return_value->dsc$w_length)
            limit = *num_stars;
        else
            limit = return_value->dsc$w_length;

        /* Create a string of stars of the specified length up to the limit of the
         * string size. */
        for (i = 0; i < limit; ++i)
            return_value->dsc$a_pointer[i] = '*';

        /* Pad rest of string with spaces, if necessary. */
        for (; i < return_value->dsc$w_length; ++i)
            return_value->dsc$a_pointer[i] = ' ';
    }  /*  End of Compaq C Function   */

Example 3-7 produces the following output:

    "***      "

Previous | Next | Contents | Index

privacy and legal statement