Welcome to the Third Issue of the C/370 Newsletter

IBM Corp 1993

                  370 370 370 370 370 370 370 370 370 370
              370 370 370 370 370 370 370 370 370 370 370 370
              370 370 370 370 370 370 370 370 370 370 370 370 370
         370 370 370 370 370 370 370 370 370 370 370 370 370 370
         370 370 370 370 370 370 370 370 370 370 370 370 370 370
         370 370 370 370 370 370 370 370 370 370 370 370 370 370
         370 370 370
         370 370 370
         370 370 370
         370 370 370
         370 370 370 370 370 370 370 370 370 370 370 370 370 370
         370 370 370 370 370 370 370 370 370 370 370 370 370 370
         370 370 370 370 370 370 370 370 370 370 370 370 370 370
             370 370 370 370 370 370 370 370 370 370 370 370 370
             370 370 370 370 370 370 370 370 370 370 370 370
                  370 370 370 370 370 370 370 370 370 370

C/370 Compiler News

September 1993 Volume 1 Number 3


Table of Contents

Welcome to the Third Issue of the C/370 Newsletter

  • In This Issue
  • The C/370 Preprocessor
  • Macro Definition
  • Macro Expansion Techniques
  • PPONLY
  • Conditional Inclusion
  • Source File Inclusion
  • #PRAGMAs
  • Other Directives
  • Implementation Defined Limits
  • You ask, we answer
    Missed our Previous Editions
    A word from your editor
    Coming Soon
  • Reader's Comment Form

  • Welcome to the Third Issue of the C/370 Newsletter

    This issue is going to have a different format than the other issues. The regular features of performance and portability hints and tips will be held until the next issue. In their place, we have a comprehensive article about the C/370 preprocessor.

    In This Issue

    The C/370 Preprocessor

    Like the C language, the C preprocessor grew up in conjunction with the UNIX operating system. It has come to be considered as a part of the C compilation process although it was originally (and still is) used for more than just C. Due to the lack of standardization for C before ANSI, different C implementations have spawned various versions of the preprocessor. We will outline the features and function of the ANSI preprocessor as interpreted and implemented by C/370.

    ANSI has defined preprocessing directives to enable these functions and these will be the topics covered and the sequence they are covered in.

    In order to give you as many examples as possible, the remainder of this article will be tabular. The text will be under the heading EXPLANATION and under the heading EXAMPLE will be examples or specifics related to the explanation.

    Explanation

    A preprocessing directive begins with the occurrence of the preprocessing token # which is either the first token in the program or is immediately preceded by the preprocessing token <newline>. The directive ends with the next <newline> token. Only space and horizontal tab are permitted as whitespace.

    Example

    Some valid preprocessing directives are:

      #include "fred.h"              /* Include a source file */
      #define BYTE 8                 /* Define a macro        */
      #if BYTE != 8                  /* Conditional inclusion */
        #error This cannot be S/370  /* Issue a diagnostic    */
      #endif                         /* End conditional block */
    

    Explanation

    The preprocessor has come to be considered as a part of the C compilation process. ANSI has reinforced this notion in the definition of the translation phases of a C program. Phases 3 and 4 represent the preprocessor imbedded in the compilation. An ANSI conforming compiler must behave as if each of these phases is applied one after the other, although this may not be the case. In C/370, phases 1 through 6 are performed in one pass.

    An important concept is that of the formation of preprocessing tokens at phase 3. We shall see that the early tokenization of the program has important implications.

    Example

    1. Trigraphs are replaced by single characters

    2. Continuation lines are spliced together

    3. The source is tokenized. Comments are replaced by whitespace

    4. Preprocessing directives are interpreted and macros are expanded

    5. Escape sequences in literals are replaced by single characters

    6. Adjacent string literals are concatenated

    7. The program is translated

    Explanation

    Equivalent Definitions

    These definitions are equivalent. Remember that trigraphs, continuation lines and comments are processed before the source is tokenized.

    Example

     #define SUM  (1 + ??/ |  #define SUM   (1 + 2 + 3)
                   2 + /*  |
               */  3)      |
     #def\                 |  #define HELLO "Hello World\n"
     ine HEL??/            |
     LO  "Hello \          |
     World??/n"            |
    

    Macro Definition

    Explanation

    The #define Directive defines macros.

    Example

       #define <id> <pptokens> <newline>
    
                    OR
    
       #define <id>( <parm-list> ) <pptokens> <newline>
    

    Explanation

    There are two kinds of macros. Object-like macros have no parameters. Function-like macros are defined with an associated parameter list containing zero or more parameters. There can not be any whitespace between the macro name and the left parenthesis.

    Example

    
    
    
    
    
    
    
    
       #define BUFFER_SIZE  1024
    
    
       #define ADD(a, b)    ((a) + (b))
    
       #define PANIC() {             \
         printf("Internal Error\n"); \
         exit(16);                   \
       }
    
       #define EXPR         (10 + 20)
    

    Explanation

    A macro definition remains visible until an #undef directive specifying the name of the macro is encountered. Macro definitions are not affected by C scoping rules. Macro expansion occurs before semantic analysis, so macro invocations are always expanded regardless of the context in which they appear.

    A macro may not be redefined unless it is first #undefed or unless the token sequence making up the new definition matches the current definition exactly. The whitespace must also be exactly the same. occur in the same places in both definitions.

    Example

     #define ADD(a, b)    ((a) + (b))  /*Initial definition  */
    
     #define ADD(x, y)    ((x) + (y))  /*Not the same...Error*/
    
     #define ADD(a, b)    (a + b)      /*Not the same...Error*/
    
     #define ADD(a, b)    ((a)  +  (b))/*Same tokens...OK    */
    
     #undef  ADD                       /*Undefine it         */
    
     #define ADD(a, b)    ((b) + (a))  /*Was #undefed...OK   */
    

    Explanation

    An object-like macro is invoked simply by the appearance of its name in the program. Object-like macros are expanded by replacing the macro name with its replacement list.

    Example

    GIVEN THE DEFINITION
         #define BUFSIZE 1024
    
    THEN
         char buffer[BUFSIZE];
    
    EXPANDS INTO
         char buffer[1024];
    

    Explanation

    A function-like macro is invoked by the appearance of its name followed by a list of arguments enclosed in parentheses. If the token immediately following the macro name is not a left parenthesis, then the macro is not expanded. Arguments are separated by commas. Note that commas occurring within nested parentheses do not separate macro arguments.

    Example

    GIVEN THE DEFINITION
         #define ADD(a, b)  a + b
    
    THEN
         ADD(j, k)
    
    EXPANDS INTO
         j + k
    

    Explanation

    After a macro's arguments are identified and before they are substituted, they are themselves scanned for macros to expand. This is done independently of the rest of the source. The result is that nested macro invocations behave like nested function calls.

    Example

    GIVEN THE FOLLOWING
         #define ADD(a, b)  a + b
         #define SUB(a, b)  a - b
    
    THEN
         ADD(SUB(i, j), k)
    
    RESULTS IN
         i - j + k
    

    Explanation

    After the macro has been expanded the replacement is rescanned along with the rest of the source file for more macro invocations.

    Example

    GIVEN THE FOLLOWING
         #define ONE        1
         #define PLUS1(a)   a + ONE
    
         PLUS1(j)
    
    PLUS1(j) expands into
         j + ONE
    
    THIS IS RESCANNED YIELDING
         j + 1
    

    Explanation

    If, during rescanning, an invocation of the same macro recurs, it is not expanded. This prevents infinite recursion from occurring.

    Example

    GIVEN THE FOLLOWING
         #define LEVEL1       LEVEL2
         #define LEVEL2       LEVEL3
         #define LEVEL3       LEVEL1
    
         LEVEL1
    
    THIS EXANDS INTO
         LEVEL2
    
    WHICH EXPANDS INTO
         LEVEL3
    
    WHICH EXPANDS INTO
         LEVEL1
    
    There is no further expansion due
    to recursion checking.
    

    Explanation

    As has been previously stated, macro arguments are scanned for macros to replace before they are substituted. The argument is then said to be completely macro expanded. No further expansion may take place using these tokens during rescanning.

    Example

    GIVEN THE FOLLOWING
         #define ONEMORE      1 + ONEMORE
         #define MAC1(a)      MAC2(a)
         #define MAC2(a)      a
    
         MAC1(ONEMORE)
    
    The argument ONEMORE is expanded
     before substitution yielding
    
         MAC2(1 + ONEMORE)
    
    ONEMORE is not reexpanded before
    substitution into MAC2 because
    of recursion checking.
    

    Explanation

    The stringize operator is used in conjunction with a parameter on a macro definition. When the macro is invoked, the argument is made into a string literal containing the text of the argument. White space is replaced by single blanks and any necessary escape sequences are manufactured. The argument is stringized before being scanned for macros.

    Example

    GIVEN THE FOLLOWING
       #define STR(x)       #x
       STR(Hello  /* space */ World\n)
       STR(The previous string was "Hello World\n")
       STR(The nul character is '\0')
    
    THE EXPANSION IS
       "Hello World\n"
       "The previous string was \"Hello World\\n\""
       "The nul character is '\\0'"
    

    Explanation

    These examples show a technique for expanding macros before stringizing. The first macro expands to "ONE" because stringizing is done before the argument is scanned for macros. The second macro expands to "1" because the macro ONE is expanded before being passed to STR.

    Example

    GIVEN THE FOLLOWING
         #define STR(x)       #x
         #define XSTR(x)      STR(x)
         #define ONE          1
         STR(ONE)
         XSTR(ONE)
    
    THE EXPANSION IS
         "ONE"
         "1"
    

    Explanation

    The ## operator is used to concatenate tokens. It may concatenate a token from an argument with a token in the replacement list, two tokens from two arguments, or two tokens in the replacement list. If the concatenation does not yield a valid token, C/370 will ignore the operator. When the concatenation operator is used to the right of an argument, the last token in the argument is concatenated with the first token to the right of the operator. When it is used to the left of an argument, the token to the left of the operator is concatenated with the first token of the argument. When no arguments are involved, the two tokens to surrounding the operator are concatenated. Concatenation is done before arguments are scanned for macros to replace.

    Example

    GIVEN THE FOLLOWING
         #define ASSIGNOP(op)       op  ## =
         #define PLUSOP(op)         +   ## op
         #define COMPOP(op1, op2)   op1 ## op2
         #define PLUSEQ             +   ## =
    
         ASSIGNOP(/)
         PLUSOP(+)
         COMPOP(=, =)
         PLUSEQ
    
    THE EXPANSION IS
         /=
         ++
         ==
         +=
    

    Explanation

    This shows a technique for concatenating macro expansions which is similar to the stringizing example previously shown.

    Example

    GIVEN THE FOLLOWING
         #define F                  FU
         #define B                  BAR
         #define FB                 FOOBAR
         #define CAT(a, b)          a ## b
         #define XCAT(a, b)         CAT(a, b)
    
         CAT(F, B)
         XCAT(F, B)
    
    THE EXPANSION IS
         FOOBAR
         FUBAR
    

    Explanation

    ANSI Predefined Macros

    ANSI has defined several predefined macros. The values of __LINE__ and __FILE__ vary during the translation and are affected by the #line directive. The values of __DATE__ and __TIME__ are fixed during the translation. __STDC__ may be used as a test for an ANSI conforming implementation.

    Example

    __LINE__
    A decimal literal whose value is the current line number of the current source file.

    __FILE__
    A string literal containing the name of the current source file.

    __DATE__
    A string literal containing the date of translation.

    __TIME__
    A string literal containing the time of translation.

    __STDC__
    A decimal literal whose value is 1.

    Explanation

    SAA(TM) Predefined Macros

    As an SAA language, C/370 also predefines the SAA macros. The value of __TIMESTAMP__ is not available to the compiler on MVS. A default date is supplied in its place.

    Example

    __TIMESTAMP__
    A string literal containing the date and time of the last modification to the current source file.

    __ANSI__
    Defined to be 1 when the langlvl is ANSI.

    __SAA__
    Defined to be 1 when the langlvl is SAA.

    __SAA_L2__
    Defined to be 1 when the langlvl is SAA or SAAL2.

    __EXTENDED__
    Defined to be 1 when the langlvl is EXTENDED.

    Explanation

    Using uppercase macro names allows macros to be easily identified. In this example, it is easy to distinguish between constants, variables, and functions even though the declarations are not visible.

    Example

       if (i >= BUFSIZE)
         newsize = EXTEND(buf);
       i = add_to_buf(data);
    

    Explanation

    Using function-like macros for non constants allows the programmer to distinguish between manifest constants and macros which may have side effects. If FIRSTCHAR had been defined as object-like, it would have appeared to be just another constant.

    Example

     #define BUFSIZE     1024
     #define FIRSTCHAR() (getc() == '0' ? '1' : '0')
     maxbuf   = BUFSIZE;
     initchar = FIRSTCHAR();
    

    Explanation

    In this example, it is not clear that the variable is_allocated is modified.

    Example

       #define BUFSIZE 1024
    
       #define ALLOC(buf) {             \
         buf = malloc(BUFSIZE);         \
         is_allocated = (buf != NULL);  \
       }
    
       .
       ALLOC(mybuf);
    

    Explanation

    Now it is clear that is_allocated is affected and should probably be tested.

    Example

       #define BUFSIZE 1024
    
       #define ALLOC(buf) (    \
         buf = malloc(BUFSIZE) \
       )
    
       .
       is_allocated = (ALLOC(mybuf) != NULL);
    

    Explanation

    Parenthesizing macros which are expressions ensures proper operator binding. The programmer may believe that buf1 is large enough for an array of 10 buffers, but it is not.

    Example

     #define BUFSIZE1     1024 + 4
     #define BUFSIZE2    (1024 + 4)
     buf1 = malloc(BUFSIZE1 * 10); /*Allocates 1064 bytes*/
     buf2 = malloc(BUFSIZE2 * 10);  /*Allocates 10280 bytes*/
    

    Explanation

    Parenthesizing macro parameters also helps to ensure correct binding of operators. The programmer may believe that buf1 is large enough for an array of 2 buffers, but it is not.

    Example

       #define TIMES2(a)  (a * 2)
       #define TIMES3(a)  ((a) * 3)
       buf1 = malloc(TIMES2(100 + 4));  /* Allocates 108 bytes */
       buf2 = malloc(TIMES3(100 + 4));  /* Allocates 312 bytes */
    

    Explanation

    Bracing statement macros will ensure the proper formation of compound statements. The second statement of INCR is not conditioned by the if statement as intended.

    Example

       #define INCR(a, b) \
         (a) += (b);      \
         (b) += 10;
    
       #define DECR(a, b) { \
         (a) -= (b);        \
         (b) -= 10;         \
       }
    
       if (i < j)                 if (i < j)
         INCR(i, j);                (i) += (j); (j) += 10;;
                       BECOMES
       if (i < j)                 if (i < j)
         DECR(i, j);                { (i) -= (j); (j) -= 10; };
    

    Explanation

    Bracing invocations of statement macros will avoid the syntax error in the this example.

    Example

       #define INCR(a, b) { \
         (a) += (b);        \
         (b) += 10;         \
       }
       #define DECR(a, b) { \
         (a) -= (b);        \
         (b) -= 10;         \
       }
       if (i < j)                  if (i < j)
         INCR(i, j);    BECOMES      { (i) += (j); (j) += 10; };
       else                        else
         DECR(i, j);                 { (i) -= (j); (j) -= 10; };
                                                               |
                                              Syntax Error -----
    
       if (i < j) {     BECOMES    if (i < j) {
         INCR(i, j);                 { (i) += (j); (j) += 10; };
       }                           }
       else {                      else {
         DECR(i, j);                 { (i) -= (j); (j) -= 10; };
       }                           }
    

    Explanation

    A macro argument may be substituted any number of times. This can cause the macro invocation to produce undefined expressions as in the first example, or unexpected side effects as in the second example. Expressions with side effects should be assigned to a temp which may then be passed to the macro.

    Example

     GIVEN
      #define MAX(a, b) ((a) > (b) ? (a) : (b))
    
      MAX(i++, j)
    
     BECOMES
      ((i++) > (j) ? (i++) : (j))
    
     AND
    
      MAX(getnum(),10)
    
     BECOMES
      ((getnum()) > (10) ? (getnum()) : (10))
    

    Macro Expansion Techniques

    Explanation

    For macros which do not return a value, temporary automatic storage may be used in order to evaluate a macro argument only once. This provides an inherent way to avoid side effects.

    Example

     #define DOUBLE_SQUARE(a, b, c) { \
       int temp = (a);    \
       (b) = temp * 2;    \
       (c) = temp * temp; \
     }
    
     DOUBLE_SQUARE(getnum(), i, j)
    
    BECOMES
    
     { int temp = (getnum()); (i) = temp * 2; (j) = temp * temp; }
    

    Explanation

    A function like macro is only expanded provided that the argument immediately follows the macro name. If a name is defined as both a function and a macro, the function can still be accessed as shown below.

    Example

       int max(int a, int b) {
         return(a > b ? a : b);
       }
    
       #define max(a, b) ((a) > (b) ? (a) : (b))
    
       .
       i = max(j, k);    /* Calls macro */
    
       i = (max)(j, k);  /* Calls function */
    
       p = max; /*p get address of function max*/
    

    PPONLY

    Explanation

    C/370 has a compile time option called PPONLY which instructs the compiler to run the preprocessor only and to dump the output to a file. This file may then recompiled. There are some instances in which the meaning of a program can be changed by this process. The instance above could have been prevented by properly parenthesizing ++x in PREINC.

    Example

    GIVEN
         #define PREINC(x)  ++x
    
    THEN
         i = j+PREINC(k);
    
    BECOMES
         i = j+++k;  /*Tokenized as j + ++ k ;*/
    
    WHEN RECOMPILED
         i = j+++k;  /*Tokenized as j ++ + k ;*/
    
    PREVENTION USE
         #define PREINC(x)  (++(x))
    

    Explanation

    C/370 Version 2.1 or AD/Cycle(TM) C/370 1.1 offer a slightly differen t variation on PPONLY, called PPONLY(*). PPONLY(*) differs from PPONLY in that it (1) inserts #line directives and (2) inserts spaces between tokens to allow it to be recompiled.

    Example

    GIVEN
         #define PREINC(x)  ++x
    
    THEN
         i = j+PREINC(k);
    
    BECOMES
         i = j+ ++k;  /* Tokenized as j + ++ k ; */
    
    WHEN RECOMPILED
         i = j+ ++ k; /* Tokenized as j + ++ k ; */
    
    ( )

    Conditional Inclusion

    Explanation

    Conditional inclusion allows the compiler to include or exclude lines of a program based on some expression which can be evaluated at compile time. This is a useful technique for writing system specific portions of portable applications and for debugging.

    A block of code is included if its controlling expression evaluates to TRUE (non-zero) and excluded otherwise. If the expression is FALSE (zero) and there is an #else group, then it is included. #elif is a convenient way to say #else #if without adding an additional level of nesting. There may be more than one #elif group. #endif is used to control the nesting of the logic.

    Example

       #if <expression> <newline>
         ...code...
       #elif <expression> <newline>
         ...code...
       #else <newline>
         ...code...
       #endif <newline>
    

    #if statements can be nested. Each #if must be paired with an #endif. In this example, the names C370 and MVS could have been previously defined using #define or the DEFine compiler option.

    Example

       #if C370
          #if MVS
             printf("This is MVS\n");
          #else
             printf("This is VM\n");
          #endif
       #endif
    

    Explanation

    The controlling expression must be an integral constant expression, although type casts are not permitted. Macro expansion is performed on the expression before evaluation. Any identifiers that remain after macro expansion are assigned the value 0.

    Example

       #if WORD == 4 * BYTE
          ...system specific code...
       #else
          ...system specific code...
       #endif
    

    Explanation

    All types of integer literals are allowed. Arithmetic is performed according to the normal rules except that long int and unsigned long int are used instead of int and unsigned int.

    Example

       #if C370
          #define NEWLINE '\x15'  /*char  literal*/
       #elif CPO
          #define NEWLINE 0x25    /*Hex   literal*/
       #elif CSET2
          #define NEWLINE 015     /*Octal literal*/
       #endif
    
       #if NEWLINE == '\n'
          #define NATIVE 1
       #endif
    

    Explanation

    The controlling expression may contain an operand of the form defined(name) or defined name. These operands evaluate to 1 if the name is currently defined as a macro, otherwise to 0.

    Example

       #if defined(C370) || defined CPO
         ...code...
       #elif defined CSET2
         ...code...
       #endif
    

    Explanation

    The #ifdef and #ifndef directives are like the #if directive except that the controlling expression is a macro name. #ifdef name is equivalent to #if defined(name). #ifndef name is equivalent to #if !defined(name). No macro expansion is performed on these directives.

    Example

       #ifdef C370
         ...code...
       #endif
       #ifndef CSET2
         ...code...
       #endif
    

    Explanation

    The LANGLVL compiler option predefines different macros to indicate the language level setting. You can use these macros to conditionally compile code which is sensitive to the language level. Note the different ways in which the macros are tested.

    Example

       #ifdef __ANSI__
          printf("LANGLVL is ANSI\n");
       #elif defined(__SAA_L2__)
          #if __SAA__
             printf("LANGLVL is SAA\n");
          #else
             printf("LANGLVL is SAAL2\n");
          #endif
       #else
          printf("LANGLVL is EXTENDED\n");
       #endif
    

    Explanation

    In C, an external variable shared by multiple files must have only one DEFINITION among those files. It may have more than one DECLARATION. The technique shown here can be used to accomplish this with one #include file. For the main file, the keyword extern is replaced by nothing, thus creating a definition of the variable with initialization. For all other files, a declaration with no initialization is produced.

    Example

     In the file "global.h" | In the file "main.c"
                            |
       #ifdef MAIN          | #define MAIN
         #define extern     | #include "global.h" /* Definition */
       #endif               |
                            |
       extern int extvar    | ____________________________________
       #ifdef MAIN          |
         = 0                | In the file "other.c"
       #endif               |
       ;                    | #include "global.h" /*Declaration*/
                            |
       #ifdef MAIN          |
         #undef extern      |
       #endif               |
    

    Explanation

    Macros can be used with conditional compilation in order to aid debugging during development. In the example above, if DEBUG is TRUE then the call to malloc will be transformed into a trace of malloc. Also calls to DBG and assert will be made. If DEBUG is FALSE, then malloc will be called directly and the DBG and assert calls will be ignored.

    Example

       #ifdef DEBUG
         #define DBG(x)    printf x
         #define malloc(n) {                                 \
           printf("malloc(%i) called at %i of "__FILE__"\n", \
                  (n), __LINE__);                            \
           malloc(n);                                        \
         }
       #else
         #define DBG(x)
         #define NDEBUG
       #endif
    
       #include <assert.h>
       #include <stdio.h>
       .
       .
       p = malloc(1024);
       DBG(("p == %p\n", p));
       assert(p != NULL);
    

    Source File Inclusion

    Explanation

    The #include directive is used to insert the contents of another file into the compilation. There are two types of #include files. System #includes use angle brackets, and user #includes use double quotes. The difference is in the search path. There is a search path for system #includes which is searched for #includes using <>. When the #include uses "", the search path for user #includes is attempted. If the file is not found, the system search path is attempted. Macro expansion is performed before the #include directive is interpreted.

    Example

       #include "filespec" <newline>
    
                OR
    
       #include <filespec> <newline>
    

    Explanation

    VM #include File Transformations

    1. The filespec is stripped from the left of all characters up to and including the rightmost /.
    2. The remaining filespec is parsed as
      filename <filetype <filemode>>.
      
      periods may separate the components.
    3. The filename and filetype are truncated to 8 characters, the filemode to 2 characters. All are uppercased.
    4. The default filetype is H, the default filemode is *.

    Regardless of the type of #include, some transformations are made on the contents of the #include header.

    Example

       #include "C:/headers/global.h"  BECOMES   "GLOBAL.H.*"
       #include <stdio.h>                        <STDIO.H.*>
       #include "fred"                           "FRED.H.*"
       #include "/headers/global.h.i"            "GLOBAL.H.I"
    

    Explanation

    MVS #include File Transformations

    1. The filespec is stripped from the left of all characters up to and including the rightmost /.
    2. The remaining filespec stripped from the right of all characters up to and including the leftmost period of blank.
    3. The remaining filespec is truncated to 8 characters and uppercased. All underscores (_) are replaced by @.

    Example

       #include "C:/headers/global.h"  BECOMES  "GLOBAL"
       #include <stdio.h>                       <STDIO>
       #include "fred"                          "FRED"
       #include "/headers/global.h.i"           "GLOBAL"
    

    Explanation

    VM #include Search Paths

    On VM, if a full filespec is specified, no search is made. The file is accessed directly. Otherwise a search is made for a file matching the filename and filetype. The default filetype is H.

    If SEARCH and LSEARCH are not specified, then the standard CMS search order is used. (L)SEARCH can be used to limit the search. SEARCH limits the search for system #includes, and LSEARCH limits the search for user #includes. The operand of (L)SEARCH specifies a file mode where the search should begin.

    Example

    Given the options SEARCH(v) LSEARCH(X)
      the search order would be
    
          System Files <>        User Files ""
    
                V                     X
                W                     Y
                X                     Z
                Y                     V
                Z                     W
    

    Explanation

    MVS #include Search Paths

    On MVS, #include files are members of partitioned data sets. If SEARCH and LSEARCH are not specified, the user search path is the set of datasets associated with the ddname USERLIB and the system search path is the set of datasets associated with the ddname SYSLIB. For example (see foil).

    On MVS (L)SEARCH takes a list of PDSs as arguments. These datasets are prepended to the appropriate search paths.

    Example

    GIVEN THE FOLLOWING JCL
    
         //SYSLIB   DD DISP=SHR,DSN=ABC.A
         //            DISP=SHR,DSN=ABC.B
         //USERLIB  DD DISP=SHR,DSN=XYZ.A
         //            DISP=SHR,DSN=XYZ.B
    
    AND THE OPTIONS SEARCH('BB.D', 'BB.F') LSEARCH('CC.X')
    
    THEN THE SEARCH ORDER WOULD BE
    
          System Files <>        User Files ""
    
              BB.D                   CC.X
              BB.F                   XYZ.A
              ABC.A                  XYZ.B
              ABC.B                  BB.D
                                     BB.F
                                     ABC.A
                                     ABC.B
    

    #PRAGMAs

    Explanation

    The #pragma directive is for use by implementations to define their own preprocessing directives. The syntax of each directive and its behavior is implementation defined. C/370 allows more than one directive to be specified on the same #pragma.

    For portability, unrecognized #pragmas are flagged only as a warning by C/370. C/370 does not perform macro expansion on #pragma directives.

    Example

       #pragma <pptokens> <newline>
    

    Explanation

    SAA #pragmas

    Here is a list of SAA defined #pragma directives. It is provided in order to convey an idea of what is available. For details see the SAA Common Programming Interface C Reference - Level 2.

    Example

    page
    Insert page ejects into the listing

    title
    Change the title on the listing

    subtitle
    Change the subtitle on the listing

    skip
    Insert blanks lines into the listing

    pagesize
    Set the page length of the listing

    comment
    Insert a comment into the object code

    langlvl
    Set the language level for a compilation

    strings
    Specify readonly or writeable strings

    chars
    Specify signed or unsigned char as default

    map
    Specify mapping for external symbols

    linkage
    Specify the linkage convention used by a function

    Explanation

    C/370 #pragmas

    Here is a list of C/370 defined #pragma directives. It is provided in order to convey an idea of what is available. For details see the IBM(TM) C/370 User's Guide.

    Example

    options
    Set default compile time options

    runopts
    Set default run time options

    variable
    Specify reentrant and non-reentrant variables

    margins
    Specify MARGINS setting for a block of code

    sequence
    Specify SEQUENCE columns for a block of code
    ( )

    Other Directives

    Explanation

    The #line Directive

    The line directive changes the line number and optionally the name of the source file usually generated by code generators and other tools in order to relate messages to the original source. This directive also affects the values of the __LINE__ and __FILE__ predefined macros. Macro expansion is performed before the directive is interpreted.

    Example

       #line <decimal-integer> <newline>
    
                 OR
    
       #line <decimal-integer> <string> <newline>
    

    Explanation

    The #error Directive

    The #error directive causes the compiler to generate an ERROR level diagnostic containing the text of the remaining tokens on the line. Macro expansion is not performed on this directive.

    Example

       #error <pptokens> <newline>
    
       #if C370
         ...code...
       #elif CSET2
         ...code...
       #else
         #error System specific code needed
       #endif
    

    Explanation

    The null Directive

    The null directive has no effect.

    Example

       #
    

    Implementation Defined Limits

    Explanation

    ANSI has defined limits which a conforming implementation must meet or exceed. Listed above are those categories which relate to the preprocessor along with the C/370 limit if any.

    The #include nesting limit is controllable (in Version 2.1 or AD/Cycle(TM) ) via the NESTINC option. Using NESTINC(*) specifies that there is no fixed limit.

    Example

    Conditional Inclusion Nesting
    No limit

    Internal Name Length
    128

    Number of Macros defined
    No limit

    Number of Macro Parameters
    No limit

    Number of Macro Arguments
    No limit

    Length of Logical Source Line
    No limit

    Length of Physical Source Line
    32760 for MVS, 65535 for VM

    Length of String Literal
    4096 bytes

    #include Nesting
    16 (user-controllable)
    ( )

    You ask, we answer

    These questions are culled from a variety of sources: from various question and answer databases; from customer calls; and from our own experiences.

    If you have a question you like to ask us, have a comment about C/370, or just want to subscribe to this newsletter, please use the Reader's Comment Form at the back of this newsletter.

    Question- Run-time libraries for C version 2 compiler

    Can I run the programs compiled in C version 2 compiler with the version 1 C run-time libraries?

    Answer

    No. C/370 provides upward compatibility only. Programs compiled with Version 1 can be run with the Version 2 libraries, but not the other way around.

    Question- Passing parameters in 24-bit address space

    I've got a program linked as AMODE(31) RMODE(ANY), and I must fetch() and call another load module which is AMODE(24). The load and call works fine, but the parameters I pass to the program have 31-bit addresses.

    I'm using the #pragma for the OS linkage. Is there some way I can tell the C compiler to move the arguments (char *) into 24 bit address space.

    Answer

    You should specify HEAP BELOW and STACK BELOW runopts. The Stack will default to BELOW. Using these runopts causes local vars, writable static, and storage acquired by malloc()/calloc()/realloc() to come from below the line. This will not handle passing addresses of functions nor passing addresses of static variables in a module where you haven't run the prelinker. The reason for this is that your module will be loaded above the line if possible which will cause module addresses to be above the line and when you do not compile RENT and prelink your module, your static variables, etc will be located adjacent to the code which is running above the line. If you think you might be passing any addresses, you should compile RENT and prelink.

    Question- Multiple pre-inits

    Can anyone tell me if it is possible to have two or more preinitized C programs running at the same time. I have tried with 2 programs but always abend when issuing the terminate call for the second C program. All tests were carried out under VM/ESA(TM) using C/370 v2r1m0.

    Answer

    Multiple pre-init is NOT supported under C/370 ver 2.1.

    Question- Is there a "packing" keyword for structures

    I have the following:

    struct mystruct
    {
        long x;
        long y;
        char xc;
        char yc;
    };
    
    where I want its size to be 10 bytes instead or 12. Can I do this without doing something like declaring x and y as char [4]?

    Answer

    You can use "_Packed" keyword which is described in CPI reference manual SC09-1308-2. Pages 69, 73. If you run the following test case you will see that the packed structure is 10 bytes long.

    #include <string.h>
    #include <stdio.h>
    main () {
     struct mystruct {
        long x;
        long y;
        char xc;
        char yc;
     };
    
    _Packed struct mystruct pack;
            struct mystruct reg;
     printf("the length of packed  structure is %d\n", sizeof(pack));
     printf("the length of regular structure is %d\n", sizeof(reg));
    }
    

    Question- Calling a 'C' function from other languages

    Calling a 'C' function from other languages.

    I am trying to call a 'C' program from PL/1. What are the parameters I have to pass to 'C' to setup the 'C' environment ?.

    Answer

    When calling a C function from PLI you should use the #pragma linkage(funcname,PLI) in order for parameters to be passed correctly.

    Question- DDname substitution lists

    Can someone please clarify whether C version 2 compiler supports DDname substitution lists or not? If yes, where can I find detailed documentation?

    Answer

    By "DDNAME Substitution Lists" do you mean the ability to compile using an assembly routine to "remap" the ddnames ( occasionally referred to as alternate ddnames ).

    If so, look up Dynamic Compilation in the V2R1 manual ( p.61 ). and there is a short example. The next thing you should do is find a macro reference ( I used the System Programming Library: Application Development Macro Reference(GC28-1857-5)) and read about the LINK / ATTACH / CALL macros to decide which one is for you.

    Question- Reading VSAM records backwards

    Anyone know of at least a marginally efficient means of scanning a VSAM dataset backwards in C/370? If the origin parameter for fseek() is SEEK_END, the offset parameter has to be an encoded one from ftell()...

    Answer

    First, fopen the file with "type = record".

    Then you could either: - specify acc=bwd on the fopen() call; - call flocate() with the KEY_LAST option; Either of these will put you at the end of the dataset, ready to read backwards. For more info, see chapter 26 of the C/370 V2R1 Programming Guide, or see the AD/Cycle(TM) C/370 Programming Guide (Chapter 15 in the Release 1.0 book). There is NO mention made of the acc= option in the SAA C book.

    The SAA book was trying to document SAA C, i.e., the stuff that was common to all the platforms, and acc=bwd is strictly a C/370 extension to SAA C for VSAM.

    Question- Why does release() require a pointer to a non-fetchable C function?

    The only reason to use release() is to delete a function or entry point created by fetch() or fetchep() which is by definition fetchable. This seems to be an inconsistent use of data types! You would never want to release() a non-fetchable function anyway.

    Answer

    The release function is used for non-C functions as well. For non-C functions, you can't have a pointer declared with linkage fetchable, so the release() argument is set up to require the pointer to a C function returning void.

    Question= Fetch()

    When using the fetch() call, to get it to work, I have to specify the entry point to the link step (pragma linkage not enough I guess), and link it with the C runtime library. Is it possible not to specify the C runtime, link with NCAL, and have the load module resolve the C stuff when fetched?

    Answer

    You don't link in the runtime library, you link in stubs out of the SEDCBASE library. These stubs are next to nothing and merely know how to find their way to the real library code at runtime (dynamic library SEDCLINK loaded at runtime).

    If you use #pragma linkage (x, fetchable) where x is the name of the function you wish to fetch (you specify this in the source where x resides), this should generate a CEESTART for you and a special CSECT which is referenced by CEESTART. When you link, CEESTART should be your default entry point and everything should work fine.

    If you do not use #pragma linkage (,fetchable), then you are forced to provide an entry point that fetch can use. Note that writable static is not an option using this method.

    Question- COBOL II Issuing a static call to a "C" language program

    We have a COBOL II program, and a C program. We are trying to issue a static call to a C program, from the COBOL II program. We compiled C program, and included it at COBOL II deck to linkedit together, as a single load module. But at linkedit time, we have a lot of unresolved to this C sub-routine. Our environment is CICS/MVS(TM) and this CICS (211) does not support C language. Our question is: May I issue a static call to a C language sub-routine from a COBOL II program in a CICS environment ??

    Answer

    Yes, you should be able to do this. I would suggest you code the C sub-routine (aka function) as an 8 character alphanumeric name in uppercase. Do the same in COBOL.

    If you haven't already done so, I would also suggest that you read the chapter in the C/370 Programming Guide that explains how to do ILC's of COBOL<->C (SC09-1384-00 for V2R1 of C/370 or SC09-1356-01 for AD/Cycle(TM) C/370 V1R1).

    By the way, if you are doing lots of ILC's between COBOL and C and you are not using AD/Cycle(TM) C/370 V1R1, you may want to consider getting AD/Cycle(TM) C/370 V1R1 - ILC's between COBOL and C are much faster in the new release, and there is a multi-lingual debugger, Debug Tool, which works under this environment and supports debugging COBOL and C.

    Question- Type Conversion

    Reading a file, which was created as a VSAM file via a COBOL program, as a flat file user is experiencing a problem trying to read a FLOAT(2) item trying to read data that was declared as COMP-3 in COBOL.

    Answer

    Check the V2.1 Programming Guide (SC09-1384-00) Page 324 or the AD/Cycle(TM) LE/370 V1R1 Programming Guide (SC26-4818-00) Page 144. This lists the data types that can be shared between COBOL and C/370. Unfortunately, COMP-3 is not one of them. ( )

    Question- Text Deck

    We want to ensure that a certain text deck gets put at the front of the aggregate text deck generated by the prelinker. I presumed we would do this by just putting the INCLUDE for the text deck first in the input file we feed the prelinker. However, I noticed that some one has put an ORDER statement for the text deck in some of our prelinker input files and it isn't creating any error messages. Does this mean that the prelinker supports ORDER cards like the MVS linkage editor does?

    Answer

    From Version 2.1 Programmer's Guide (SC09-1384-0, page 88):

      "The only job control statements processed by the prelink utility
       are INCLUDEs, LIBRARYs and RENAMEs. The remaining control statements
       are left unchanged until the link step."
    
    I.E. the ORDER card will be passed to the linkage editor.
    

    Question-Retrieving a userid I want to retrieve a userid under MVS (batch, ISPF etc) from a C program. Has anyone done this? Alternatively, does anyone know where I could look for more information?

    Answer

    Without getting into the assembler method of doing this, try the following trick:

    Under TSO, this will get the TSO prefix. On a non-RACF system, this does not work. You can figure this out though since C/370 will not add qualifiers to your unqualified names that you open.

    Question- IVPCOMP

    I attempted to compile the sample program IVPCOMP that displays the installed version. I get........

    DMSITP142T Operation exception occurred at 80C73F42 in routine EDCC210 during
    SPIE exit routine
    DMSITP142T Operation exception occurred at 80C72106 in routine SVC 13 during
    SPIE exit routine
    DMSABN148T System abend 0C1 called from 00C72106 reason code 00000000
    

    Does anyone know what the problem may be?

    Answer

    Removal of PLILIB in the GLOBAL TXTLIB should solve your problem.

    Question- load() function

    Does C/370 support the load() function?? If so, is there a ldr.h file???

    Answer

    C/370 does not have a load() function nor a ldr.h header file.

    Question CMS commands from C

    Can I issue CMS commands from C???

    Answer

    Yes. Try rc = system ("command string");

    Question- Determine type of compiler

    I want to be able to determine the type of compiler from within a program. Is there a way that they can ask "Am I C/370?", at compile time and/or at run time?

    Answer

    The __librel function (documented in the C/370 Programming Guide) will return the release level of the library. The top 2 bits returned will be the product code. For C programs running under LE/370, it is 1. Please refer to __librel to find out if it will suit your needs.

    Question- Bug or Feature

    Bug or Feature?

    I thought one of the features of C was typechecking. Imagine my surprise when I ran the following program:

    /* C typechecking test */
    
    int e (short int);
    
    int main() {
    
       int i = 12345678;
       printf ("e(12345678) is %d\n",e(i));
       return 0;
    
    }
    
    int e (short int i) { return i; }
    

    Not only does it compile with nary a whisper of complaint, it happily truncates during execution without giving any indication that something might be amiss. The only thing I can say in C/370's defense on this is that it seems consistent with other C implementations (RS/6000 has the same useful behavior).

    Can you offer a rationale for this?

    Answer

    You can get the warning you want by using the (CHECKOUT(NONE,ACC) option.

    It's not obviously an error - there are times when you'd want to assign a narrow number from a wide one, confident that there'd be no overflow. Hence, a warning is appropriate rather than an error.

    If you'd written e((short)i), it would have been obvious (to the compiler!) you knew what was going on, and intended it, so warnings would be suppressed.

    Question- Sequence numbers in head files

    Sequence numbers in MVS TCP/IP header files

    I am migrating a set of AIX C programs that work with TCP/IP to MVS. The header files that come with the TCP/IP product on MVS, have sequence numbers in columns 72-80. My C programs have not, they use the full 80 columns. So, I normally compile with NOSEQUENCE, NOMARGINS. But now the C compiler chokes on the very first sequence number in the include file. I have tried to prevent that with some pragma's (see below) , but that does not seem to work. Does anybody spot what I am missing?

    #ifdef MVS
       /* TCP/IP include files have seqnumbers in cols. 73-80 */
       #pragma sequence(73,80) margins(1,72)
          #include <bsdtypes.h>
    
    /* $Header:types.h 9.1$ */
    
    =====> a - EDC0350 Syntax error.
    
    /* $ACIS:types.h 9.1$ */
    /* $Source: /ibm/acis/usr/sys/h/RCS/types.h,v $ */
    
    #ifndef _TYPES_
    #define  _TYPES_
         .
         .
    u_long    in_netof(), in_lnaof();
    #endif
       #pragma nosequence nomargins
    #endif
    

    Answer

    The problem is that each header file starts with the margins and sequence specified in the command line options and that the #pragma only applies to the file that it occurs in. This makes sense since one never really knows the characteristics of the other headers in an application.

    With this in mind, you can do one of two things:

    1. Place your own code into RECFM=V datasets. The default is NOMARGINS NOSEQUENCE for these files.

    2. Place
            #pragma nomargins nosequence
      

      into each of your own files. This will not affect the TCP/IP headers.

    In either case you will no longer need to specify NOMARGINS NOSEQUENCE in the compiler options.

    Question- Message EDC0322/EDC0141/EDC147

    I have a C/370 program which utilizes fetch() and release() functions. Under C/370 V1R2M0 there was no need to cast a function pointer in the release() function calls. However, the same program compiled under C/370 V2R1M0 requires a cast. I have defined the following fetchable function types:

     typedef void ASM_VOID ();
     #pragma linkage(ASM_VOID,OS)
     typedef void C_VOID ();
     #pragma linkage(C_VOID,FETCHABLE)
     typedef void COBOL_VOID ();
     #pragma linkage(COBOL_VOID,COBOL)
     typedef void FORTRAN_VOID ();
     #pragma linkage(FORTRAN_VOID,FORTRAN)
     typedef void PLI_VOID ();
     #pragma linkage(PLI_VOID,PLI)
     typedef union {
       ASM_VOID      *ASM_PTR;
       C_VOID        *C_PTR;
       COBOL_VOID    *COBOL_PTR;
       FORTRAN_VOID  *FORTRAN_PTR;
       PLI_VOID      *PLI_PTR;
     } LANG_PTR;
    

    Under C/370 V1R2M0 here is the code for fetch()/release() functions:

       COBOL_VOID           *iortn;
       COBOL_VOID           *scrnrtn;
       iortn = (COBOL_VOID *) fetch(IORTN_NAME);
       scrnrtn = (COBOL_VOID *) fetch(SCRNRTN_NAME);
       ...
       release(iortn);
       release(scrnrtn);
    

    Under C/370 V2R1M0 the same code receives messages:

     EDC0322 "Type of the parm  cannot conflict with previous declaration"
     EDC0141 "Prototype has type pointer to  C function returning  void"
     EDC0147 "Argument has type pointer to  COBOL function returning  void"
    

    The code was then revised to:

       release((C_VOID *) iortn);
       release((C_VOID *) scrnrtn);
    

    And compiled under C/370 V2R1M0 which received messages:

     EDC0322 "Type of the parm  cannot conflict with previous declaration"
     EDC0141 "Prototype has type pointer to  C function returning  void"
     EDC0147 "Argument has type pointer to  function returning  void"
    

    The following function typedef was added:

     typedef void (*FETCH_PTR) ();
     #pragma linkage(FETCH_PTR,FETCHABLE)
     The code was then revised to:
       release((FETCH_PTR) iortn);
       release((FETCH_PTR) scrnrtn);
    And compiled under C/370 V2R1M0 which received message:
    EDC0446 "Only one FETCHABLE function may be specified"
    

    Why is the cast "(C_VOID *)" not equivalent to "(FETCH_PTR)"? Type "C_VOID" is a VOID C FETCHABLE function and (C_VOID *) is a pointer to a VOID C FETCHABLE function.

    Answer

    The compiler ignores the "pragma linkage(FETCH_PTR,FETCHABLE)" as indicated by the EDC8446 message. "FETCH_PTR" is considered a normal C function, and is a different type than "C_VOID", which does have fetchable linkage.

    Question- VB file that has a null record

    I have a VB file that has a NULL RECORD.

    Fread by-passes the record and read the next record.

    Answer

    The C370 product does not support I/O processing for null or blank record's. The current behavior is to skip the record and get the next record. This will occur with fread, getc, gets etc...

    The C library uses the same subroutine in all the above cases and the subroutine will by pass the null record. Please refer to the V2.1 Programming Guide (SC09-1384-00) page 189. The only work around was to open the file as recfm=u.

    Question- Packed Structure alignment

    I remember reading, either in a C/370 manual or somewhere, that the alignment of an entire _Packed structure (the structure itself, not any of its members) would be determined by its largest member. Is this supposed to be true? Or is this true only on non-_Packed structures?

    For example, given the below structure:

      _Packed struct X {
        char a;
        int b;
        short int c;
      } x;
    
    ... will the variable "x.a" always be aligned on a word boundary?

    Answer

    The alignment that is assigned to the struct or union will be the alignment of its most strictly-aligned member (size has nothing to do with it). This is true in both _Packed and non-packed structures.

    Applying this rule, since all the members of _Packed struct X are byte-aligned (because of _Packed), your struct variable x will have an alignment of 1.

    C/370 always places variables on at least a word boundary. As long as x is not a member of another struct or union, it will be placed on a word boundary even though it is byte aligned.

    If you were to nest your _Packed struct declaration inside another structure, like

       struct Y {
         char glorp;
         _Packed struct X { ... } x;
       } y;
    

    then the member x would NOT be placed on a word boundary. Similarly, if x were an array, as in

       _Packed struct X { ... } x[2] ;
    
    then x would be placed on a word boundary, but x[ 1] would not be (since your struct X is 7 bytes long).

    Question- Challenge

    I challenge someone to come up with a structure that they can guarantee will be word aligned and each member in the structure must be byte- aligned (that is, have no padding). No unions please. (I know how to do the above combining unions and using _Packed.)

    Answer

    About the only thing you can do to achieve your goal is to

    1. Don't use _Packed

    2. Sort your members, placing members with the strictest alignment at the beginning, and those with the least alignment at the end.

    Your structure will then be aligned on at least a word boundary, although it will probably have a few bytes of padding at the end. If the struct contains doubles or long doubles, it will be aligned on an 8-byte boundary.


    Missed our Previous Editions

    If you did not get a copy of either our First or Second edition, just ask and we will be more than happy to send you a copy.


    A word from your editor

    If you haven't already sent for your free subscription to this newsletter, now is the time to send it in. We really thank the many of you who have already sent it in. We'd like to know your comments about this newsletter and our products. If you prefer, just put your business card in an envelope and mail or fax it to us at the address/phone number on the readers comment form.

    For the many of you sending in the reply forms with your comments, we may have the need to call you to discuss your comments further. It's a great help if you include your phone number. Thanks!


    Coming Soon

    Well, that's all we can squeeze in for now. In future issues, we'll bring back some performance and porting tips as well as answering some more of your questions. Thanks for reading and please let us know what you think of this newsletter.

    +----------------------------------------------------------------+
    

    This newsletter was produced by the C/370 Planning department of the IBM Software Solutions Toronto Laboratory. For further information on any of the products mentioned, please contact your local IBM office, or an authorized IBM Business Partner.

    Numerous product references in this publication are registered trademarks or trademarks of International Business Machines Corporation . IBM Canada Ltd., a related company, is a licensee.

    This newsletter was created and marked for processing using IBM BookMaster(TM) (Program Number 5688-015) and IBM Document Composition Facility (DCF). (Program Number 5748-XX9).

    The final copy was printed on an IBM 3825 Page Printer, an Advanced Function Printer.

    This newsletter is © Copyright IBM Corporation 1993.

    In Canada - © Copyright IBM Canada Ltd. 1993. +----------------------------------------------------------------+


    C/370 Compiler News Volume 1 Number 3 September/93 Issue

    Reader's Comment Form

    1. Did you find this newsletter useful?

    2. Is there any way you think we could improve this newsletter?

    3. Is there any C/370 compiler-related subject you would like to see addressed in this newsletter?

    Please note:

    Thank you for your cooperation and help. You can either mail this form to us, or hand it into an IBM office for forwarding.

    You can also fax the form to us. Our fax number is 416-448-6057. Please mark your fax for the attention of Gord Sinclair. Thanks.

    C/370 Compiler News Volume 1 Number 3 September/93 Issue




    Footnotes:

    ( ) Products marked (TM) are trademarks or registered trademarks of IBM Corporation.