In addition to the storage-class specifiers described in the DEC C Language Reference Manual, the VAX C compatibility mode of DEC C provides the globaldef, globalref, and globalvalue storage-class specifiers. These specifiers allow you to assign the global storage classes to identifiers. The global storage classes are specific to DEC C for OpenVMS Systems and are not portable.
Use the globaldef specifier to define a global variable. Use the globalref specifier to refer to a global variable defined elsewhere in the program.
When you use the globaldef specifier to define a global symbol, the symbol is placed in one of three program sections: the $DATA (VAX only) or $DATA$ (Alpha only) psect using globaldef alone, the $CODE (VAX only) or $READONLY$ (Alpha only) psect using globaldef with readonly or const, or a user-named psect. You can create a user-named psect by specifying the psect name as a string constant in braces immediately following the globaldef keyword, as shown in the following definition:
globaldef{"psect_name"} int x = 2;
This definition creates a program section called psect_name and allocates the variable x in that psect. You can add any number of global variables to this psect by specifying the same psect name in other globaldef declarations. In addition, you can specify the noshare modifier to create the psect with the NOSHR attribute. Similarly, you can specify the readonly or const modifier to create the psect with the NOWRT attribute. For more information about the possible combinations of specifiers and modifiers, and the effects of the storage-class modifiers on program section attributes, see Section 4.8.
Variables declared with globaldef can be initialized; variables declared with globalref cannot, because these declarations refer to variables defined, and possibly initialized, elsewhere in the program. Initialization is possible only when storage is allocated for an object. This distinction is especially important when the readonly or const modifier is used; unless the global variable is initialized when the variable is defined, its permanent value is 0.
Example 4-1 shows the use of global variables.
/* This example shows how global variables are used * * in DEC C programs. */ #include <stdlib.h> int ex_counter = 0; globaldef double velocity = 3.0e10; globaldef {"distance"} long miles = 100; int main() { printf(" *** FIRST COMP UNIT ***\n"); printf("counter:\t%d\n", ex_counter); printf("velocity:\t%g\n", velocity); printf("miles:\t\t%d\n\n", miles); fn(); printf(" *** FIRST COMP UNIT ***\n"); printf("counter:\t%d\n", ex_counter);
printf("velocity:\t%g\n", velocity); printf("miles:\t\t%d\n\n", miles); exit (EXIT_SUCCESS); } /* ---------------------------------------------------- * * The following code is contained in a separate * * compilation unit. * * ---------------------------------------------------- */ static ex_counter; globalref double velocity; globalref long miles; fn(void) { ++ex_counter; printf(" *** SECOND COMP UNIT ***\n"); if ( miles > 50 ) velocity = miles * 3.1 / 200 ; printf("counter:\t%d\n", ex_counter); printf("velocity:\t%g\n", velocity); printf("miles:\t\t%d\n", miles); }
Key to Example 4-1:
Sample output from Example 4-1 is as follows:
$ RUN EXAMPLE.EXE<Return> *** FIRST COMP UNIT *** counter: 0 velocity: 3.000000e+10 miles: 100 *** SECOND COMP UNIT *** counter: 1 velocity: 1.55 miles: 100 *** FIRST COMP UNIT *** counter: 0 velocity: 1.55 miles: 100
The global storage-class specifiers define and declare objects that differ from external variables both in their storage allocation and in their correspondence to elements of other languages. Global variables provide a convenient and efficient way for a DEC C function to communicate with assembly language programs, with OpenVMS system services and data structures, and with other high- level languages that support global symbol definition, such as VAX PL/I. For more information about multilanguage programming, see Chapter 3.
DEC C imposes no limit on the number of external variables in a single program.
There are other functional differences between the external and global variables. For example:
In programming environments other than the OpenVMS environment, C programmers may be accustomed to extern declarations causing the loading of a module into the program's executable image. If transportability is an issue, you can define the following symbols-at the compilation-unit level, outside of all functions- to allocate storage differently depending on the system you are using:
#ifdef __DECC #define EXPORT globaldef #define IMPORT globalref #else #define EXPORT #define IMPORT extern #endif . . . IMPORT int foo; EXPORT int foo = 53;
One similarity between the external and global storage classes is in the way the compiler recognizes these variables internally. External and global identifiers are not case-sensitive. No matter how the external and global identifiers appear in the source code, the compiler converts them to uppercase letters (unless you compile with /NAMES=AS_IS or /NAMES=LOWERCASE). For ease in debugging programs, express all global and external variable identifiers in uppercase letters.
Another similarity between the external and global storage classes is that you can place the external variables and the global variables (optionally) in psects with a user-defined name and, to some degree, user-defined attributes. The compiler places external variables in psects of the same name as the variable identifier, viewed by the linker in uppercase letters. The compiler places globaldef{"name"} variables in psects with names specified in quotation marks, delimited by braces, and located directly after the globaldef specifier in a declaration. Again, the linker considers the psect name to be in uppercase letters.
The compiler places a variable declared using only the globaldef specifier and a data-type keyword into the $DATA (VAX only) or $DATA$ (Alpha only) psect. For more information about the possible combinations of specifiers and modifiers, and the effects of the storage-class modifiers on program section attributes, see Section 4.8.
A global value is an integral value whose identifier is a global symbol. Global values are useful because they allow many programmers in the same environment to refer to values by identifier, without regard to the actual value associated with the identifier. The actual values can change, as dictated by general system requirements, without requiring changes in all the programs that refer to them. If you make changes to the global value, you only have to recompile the defining compilation unit (unless it is defined in an object library), not all of the compilation units in the program that refer to those definitions.
An identifier declared with globalvalue does not require storage. Instead, the linker resolves all references to the value. If an initializer appears with globalvalue, the name defines a global symbol for the given initial value. If no initializer appears, the globalvalue construct is considered a reference to some previously defined global value.
Predefined global values serve many purposes in OpenVMS system programming, such as defining status values. It is customary in OpenVMS system programming to avoid explicit references to such values as those returned by system services, and to use instead the global names for those values. Example 4-2 shows the use of the globalvalue storage-class specifier.
/* This program shows references to previously defined * * globalvalue symbols. */ #include <stdio.h> globalvalue FAILURE = 0, EOF = -1; main() { char c; /* Get a char from stdin */ while ( (c = getchar()) != EOF) test(c); } /* -------------------------------------------------------- * * The following code is contained in a separate compilation * * unit. * * -------------------------------------------------------- */ #include <ctype.h> /* Include proper module */ globalvalue FAILURE, EOF; /* Declare global symbols */ void test(param_c) char param_c; /* Declare parameter */ { /* Test to see if number is valid */ if ( (isalnum(param_c)) != FAILURE) printf("%c\n", param_c); return; }
In Example 4-2, FAILURE and EOF are defined in the first module: the values are placed into the program stream. In the second module, FAILURE and EOF are declared so that their values can be accessed. As with the external and global variables, the linker converts the global symbols as uppercase letters. For ease of debugging, express these symbols in uppercase.