Skip to Main Content United States    
PRODUCTS SUPPORT SOLUTIONS SERVICES
COMPAQ SOFTWARE
Compaq C

Compaq C
User's Guide for OpenVMS Systems


Previous Contents Index

1.7.2.2 Mixing Pointer Sizes

An application can use both 32-bit and 64-bit addresses. The following semantics apply when mixing pointers:

  • Assignments (including arguments) silently promote a 32-bit pointer rvalue to 64 bits if other type rules are met. Promotion means sign extension.
  • A warning is issued for an assignment of a 64-bit rvalue to a 32-bit lvalue (without an explicit cast).
  • For purposes of type compatibility, a different size pointer is a different type (for example, when matching a prototype to a definition, or other contexts involving redeclaration).
  • The debugger knows the difference between pointers of different sizes.

1.7.3 Header File Considerations

The following general header-file considerations should be kept in mind:

  • Header files usually define interfaces with types that must match the layout used in library modules.
  • Header files often do not bind "top-level" pointer types. Consider, for example:


    fprintf(FILE *, const char *, ...); 
    

    A "FILE * fp;" in a declaration in a different area of source code might be a different size.

  • All pointer parameters occupy 64 bits in the Alpha calling sequence, so a top-level mismatch of this kind is all right if the called function does not lose the high bits internally.
  • Routines dealing with pointers to pointers )or data structures containing pointers) cannot be enabled to work simply by passing them both 32-bit and 64-bit pointers. You need to have separate 32-bit and 64-bit variants of the routine.
  • The Compaq C RTL header files and the compiler cooperatively provide dual implementations of functions that need to know the pointer size used by the caller. They have different names. The compiler automatically calls the appropriate name within the pointer-size context if the source code calls the simple name. For example, a call to malloc becomes:
    • _malloc64 if /POINTER_SIZE=64.
    • _malloc32 if /POINTER_SIZE=32.
    • malloc if /POINTER_SIZE is omitted.

    If /POINTER_SIZE is specified alone or with a value, _malloc64 or _malloc32 can be called explicitly. If /POINTER_SIZE is not specified, the program is compiled to be unaware of 64-bit pointers, and so the declarations of these alternate variants are suppressed.

Be aware that pointer-size controls are not unique in the way they affect header files; other features that affect data layout have similar impact. For example, most header files should be compiled with 32-bit pointers regardless of pointer-size context. Also, most system header files (on Alpha systems) must be compiled with member_alignment regardless of user pragmas or qualifiers.

To address this issue more generally, the pragma environment directive can be used to save context and set header defaults at the beginning of each header file, and then to restore context at the end. See Section 5.4.4 for a description of pragma environment .

For header files that have not yet been upgraded to use #pragma environment, the /POINTER_SIZE=64 qualifier can be difficult to use effectively. For such header files that are not 64-bit aware, the compiler automatically applies user-defined prologue and epilogue files before and after the text of the included header file. See Section 1.7.4 for more information on prologue/epilogue files.

1.7.4 Prologue/Epilogue Files

Compaq C automatically processes user-supplied prologue and epilogue header files. This feature is an aid to using header files that are not 64-bit aware within an application that is built to exploit 64-bit addressing.

1.7.4.1 Rationale

Compaq C header files typically contain a section at the top that:

  1. Saves the current state of the member_alignment , extern_model , extern_prefix , and message pragmas.
  2. Sets these pragmas to the default values for the system.

A section at the end of the header file then restores these pragmas to their previously-saved state.

Mixed pointer sizes introduce another kind of state that typically needs to be saved, set, and restored in header files that define fixed 32-bit interfaces to libraries and data structures.

The #pragma environment preprocessor directive allows headers to control all compiler states (message suppression, extern_model , member_alignment , and pointer_size ) with one directive.

However, for header files that have not yet been upgraded to use #pragma environment , the /POINTER_SIZE=64 qualifier can be difficult to use effectively. In this case, the automatic mechanism to include prologue/epilogue files allows you to protect all of the header files within a single directory (or modules within a single text library). You do this by copying two short files into each directory or library that needs it, without having to edit each header file or library module separately.

In time, you should modify header files to either exploit 64-bit addressing (like the Compaq C RTL), or to protect themselves with #pragma environment . Prologue/epilogue processing can ease this transition.

1.7.4.2 Using Prologue/Epilogue Files

Prologue/epilogue file are processed in the following way:

  1. When the compiler encounters an #include preprocessing directive, it determines the location of the file or text library module to be included. It then checks to see if one or both of the two following specially named files or modules exist in the same location as the included file:


    __DECC_INCLUDE_PROLOGUE.H 
    __DECC_INCLUDE_EPILOGUE.H  
    

    The location is the OpenVMS directory containing the included file or the text library file containing the included module. (In the case of a text library, the .h is stripped off.)
    The directory is the result of using the $PARSE/$SEARCH system services with concealed device name logicals translated. Therefore, if an included file is found through a concealed device logical that hides a search list, the check for prologue/epilogue files is still specific to the individual directories making up the search list.

  2. If the prologue and epilogue files do exist in the same location as the included file, then the content of each is read into memory.
  3. The text of the prologue file is processed just before the text of the file specified by the #include .
  4. The text of the epilogue file is processed just after the text of the file specified by the #include.
  5. Subsequent #includes that refer to files from the same location use the saved text from any prologue/epilogue file found there.

The prologue/epilogue files are otherwise treated as if they had been included explicitly: #line directives are generated for them if /PREPROCESS_ONLY output is produced, and they appear as dependencies if /MMS_DEPENDENCY output is produced.

To take advantage of prologue/epilogue processing for included header files, you need to create two files, __DECC_INCLUDE_PROLOGUE.H and __DECC_INCLUDE_EPILOGUE.H , in the same directory as the included file.

Suggested content for a prologue file is:


__DECC_INCLUDE_PROLOGUE.H: 
 
#ifdef __PRAGMA_ENVIRONMENT 
#pragma environment save 
#pragma environment header_defaults 
#else 
#error "__DECC_INCLUDE_PROLOGUE.H: This compiler does not support 
pragma environment" 
#endif 

Suggested content for an epilogue file is:


__DECC_INCLUDE_EPILOGUE.H: 
 
#ifdef __PRAGMA_ENVIRONMENT 
#pragma __environment restore 
#else 
#error "__DECC_INCLUDE_EPILOGUE.H: This compiler does not support 
pragma environment" 
#endif 

1.7.5 Avoiding Problems

Consider the following suggestions to avoid problems related to pointer size:

  • Write code to work with either 32-bit or 64-bit pointers by using only the /POINTER_SIZE qualifier.
  • Do bit manipulation on unsigned int and unsigned __int64 , and carefully cast pointers to and from them.
  • Heed compile-time warnings, using casts only where you are sure that pointers are not truncated.
  • Enable the optional compile-time warning (/WARN=ENABLE=MAYHIDELOSS).
  • Do thorough testing when compiling with /CHECK=POINTER_SIZE.

1.7.6 Examples

The following examples illustrate the use and misuse of 64-bit pointers.

Example 1-2 Watch Out for Pointers to Pointers (**)

/* CC/NAME=AS_IS/POINTER_SIZE */ 
 
#include <stdio.h> 
 
#pragma pointer_size 64 
char  *C[2]    = {"AB, "CD"};  /* sizeof(C) = 16            */ 
char **CPTRPTR = C; 
char **CPTR; 
 
#pragma pointer_size 32 
char  *c[2]    = {"ab, "cd"};  /* sizeof(C) = 8             */ 
char **cptrptr = c; 
char **cptr; 
 
int main (void) 
{ 
    CPTR    =  cptr;           /* No problem.               */ 
    cptr    =  CPTR;           /* %CC-W-MAYLOSEDATA         */ 
 
    CPTRPTR = cptrptr;         /* %CC-W-PTRMISMATCH         */ 
    cptrptr = CPTRPTR;         /* MAYLOSEDATA & PTRMISMATCH */ 
    puts(cptrptr[0]);          /* ab                        */ 
    puts(cptrptr[1]);          /* cd                        */ 
    puts(CPTRPTR[0]);          /* Bad address passed.       */ 
    puts(CPTRPTR[1]);          /* Fetch off end of c.       */ 
} 

Compiling Example 1-2 produces:


$ cc example1/name=as_is/pointer_size 
    cptr    =  CPTR;           /* %CC-W-MAYLOSEDATA         */ 
....^ 
%CC-W-MAYLOSEDATA, In this statement, "CPTR" has a larger 
data size than "short pointer to char".  Assignment may 
result in data loss.) 
 
    CPTRPTR = cptrptr;         /* %CC-W-PTRMISMATCH         */ 
....^ 
%CC-W-PTRMISMATCH, In this statement, the referenced type 
of the pointer value "cptrptr" is "short pointer to char", 
which is not compatible with "long pointer to char". 
 
    cptrptr = CPTRPTR;         /* MAYLOSEDATA & PTRMISMATCH */ 
....^ 
%CC-W-MAYLOSEDATA, In this statement, "CPTRPTR" has a 
larger data size than "short pointer to short pointer 
to char".  Assignment may result in data loss.) 
 
    cptrptr = CPTRPTR;         /* MAYLOSEDATA & PTRMISMATCH */ 
....^ 
%CC-W-PTRMISMATCH, In this statement, the referenced type 
of the pointer value "CPTRPTR" is "long pointer to char", 
which is not compatible with "short pointer to char". 

Example 1-3 Trivial 64-Bit Exploitation

#include <stdio.h> 
#include <stdlib.h> 
__int64 limit, count; 
size_t bytes; 
char  *cp, *prevcp; 
 
int main(int argc, char **argv) 
{ 
    sscanf(argv[1], "%d",  &bytes); 
    sscanf(argv[2], "%Ld", &limit); 
    printf("bytes %d, limit %Ld, tot %Ld\n", 
           bytes,     limit, bytes * limit); 
    for (count=0; count < limit; count++)  { 
        if (!(cp = malloc(bytes)))  { 
           printf(("Max %Ld bytes.\n", bytes * (count + 1)); 
           break; 
    } else if (!prevcp) { 
        printf("First addr %Lx.\n", cp); 
    } 
    prevcp = cp; 
    printf("Last addr %Lx.\n", prevcp); 
} 

Compiling, linking, and running Example 1-3 produces:


$ cc example2 
$ link example2 
$ example2 65536 1234567890123456 
bytes 65536, limit 1234567890123456, tot 
7121664952292605952 
First addr 226008 
Max 42663936 bytes. 
Last addr 2fc8008. 
$ 
$ cc/pointer_size=64 example2 
$ link example2 
$ example2 65536 1234567890123456 
bytes 65536, limit 1234567890123456, tot 
7121664952292605952 
First addr 1c0010010. 
Max 42532864 bytes. 
Last addr 1c2d8e010. 

Example 1-4 Preceding Example No Longer Trivial

#include <stdio.h> 
#include <stdlib.h> 
__int64 limit, count; 
size_t bytes; 
char  *cp, *prevcp; 
 
static void do_args(char **args) 
{ 
    sscanf(argv[1], "%d",  &bytes); 
    sscanf(argv[2], "%Ld", &limit); 
    printf("bytes %d, limit %Ld, tot %Ld\n", 
           bytes,     limit, bytes * limit); 
} 
 
 
int main(int argc, char **argv) 
{ 
    do_args(argv); 
    for (count=0; count < limit; count++)  { 
        if (!(cp = malloc(bytes)))  { 
           printf(("Max %Ld bytes.\n", bytes * (count + 1)); 
           break; 
    } else if (!prevcp) { 
        printf("First addr %Lx.\n", cp); 
    } 
    prevcp = cp; 
    printf("Last addr %Lx.\n", prevcp); 
} 

Compiling Example 1-4 produces:


$ cc/pointer_size=64 example3 
do_args(argv); 
....^ 
%CC-W-PTRMISMATCH, In this statement, the referenced type 
of the pointer value "argv" is "short pointer to char", 
which is not compatible with "long pointer to char". 


Chapter 2
Using OpenVMS Record Management Services

Compaq C for OpenVMS systems provides a set of run-time library functions and macros to perform I/O. Some of these functions perform in the same manner as I/O functions found on C implementations running on UNIX systems. Other Compaq C functions take full advantage of the functionality of the OpenVMS file-handling system. You can also access the OpenVMS file-handling system from your Compaq C program without using the Compaq C Run-Time Library (RTL) functions. In any case, the system that ultimately accesses files on OpenVMS systems is OpenVMS Record Management Services (RMS).

This chapter introduces you to the following RMS topics:

The file-handling capabilities of Compaq C fall into two distinct categories:

  • The Compaq C RTL functions which, with little or no modification, are portable to other C implementations
  • The RMS functions, which are not portable to other C implementations, but do provide more methods of file organization and more record access modes

This chapter briefly reviews the basic concepts and facilities of RMS and shows examples of their application in Compaq C programming. Because this is an overview, the chapter does not explain all RMS concepts and features. For language-independent information about RMS, see the following manuals in the OpenVMS documentation set:

  • Guide to OpenVMS File Applications
    This guide contains a general description of the record management services of the OpenVMS operating system, and the file creation and run-time options available.
  • OpenVMS Record Management Services Reference Manual
    This manual describes the user interface to RMS. It includes introductory information on RMS programming and detailed definitions of all RMS control block structures and macro instructions.

2.1 RMS File Organization

RMS supports three types of file organization:

  • Sequential
  • Relative
  • Indexed

The following sections describe these types of file organization.

The organization of a file determines how a file is stored on the media and, consequently, the possible operations on records. You specify the file's organization when you create the file; it cannot be changed.

However, you can use the File Definition Language Editor (FDL) and the CONVERT utility to define the characteristics of a new file, and then fill the new file with the contents of the old file of a different format. For more information, see the OpenVMS Utility Routines Manual.

2.1.1 Sequential File Organization

Sequential files have consecutive records. There are no empty records separating records that contain data. This organization allows the following operations on the file:

  • Positioning the file at a particular record, generally by sequentially moving from one record to the next.
    Direct access is also possible, either by key (relative record number) or by the record file address (RFA). However, although allowed for any file organization, access by RFA is limited to files on disk devices, and access by key is limited to disk files that also have fixed-length records. These access modes are unusual because most application programs do not keep track of record positions in sequential files.
  • Reading data from any record.
  • Writing data by adding records at the end of the file.

Sequential organization is the only kind permitted for magnetic tape files and other nondisk devices.

2.1.2 Relative File Organization

Relative files have records that occupy numbered, fixed-length cells. The records themselves need not have the same length. Cells can be empty or can contain records so the following operations are permitted:

  • Positioning the file at a particular record, usually by direct access.
    In direct access, RMS uses the relative record number---the number of a cell---as a key to locate the cell and its record; there is no need to reference other cells. RMS can also access the records sequentially by ignoring empty cells, or RMS can access the file directly with the record file address (RFA). RMS returns the RFA in a parameter block whenever it writes a record, and you can access and use the RFA to locate the appropriate record. You can access any file organization with the RFA.
  • Reading a record from any cell.
  • Deleting a record from any cell.
  • Writing a record into any cell.

Relative file organization is possible only on disk devices.

2.1.3 Indexed File Organization

Indexed files have records that contain, in addition to data and carriage-control information, one or more keys. Keys can be character strings, packed decimal numbers, and 16-bit, 32-bit, or 64-bit signed or unsigned integers. Every record has at least one key, the primary key, whose value in each record cannot be changed. Optionally, each record can have one or more alternate keys, whose key values can be changed.

Unlike relative record numbers used in relative files, key values in indexed files are not necessarily unique. When you create a file, you can specify that a particular key have the same value in different records (these keys are called duplicate keys). Keys are defined for the entire file in terms of their position within a record and their length.

In addition to maintaining its records, RMS builds and maintains indexes for each of the defined keys. As records are written to the file, their key values are inserted in order of ascending value in the appropriate indexes. This organization allows the following operations:

  • Positioning the file at a particular record by direct access.
    In direct access reads, you use either a primary or alternate key, plus a specified key value, to locate the record. In direct access writes (given a record that contains key values in the predefined positions), RMS automatically adds the record to the file and adds the primary and alternate key values to the appropriate indexes. You can also access records sequentially, where the sequence is defined by the index for a specified key. Finally, you can access records directly by RFA; RMS returns the RFA in a parameter block whenever it writes a record, and you can access and use the RFA to locate the appropriate record. You can access any file organization with the RFA.
  • Reading any record, including sequential reads controlled by a key's index.
  • Deleting any record.
  • Updating an alternate key's value, if the key's definition permits its value to change.
  • Writing records selectively, based on the value of a key and, when allowed in the key's definition, based on duplicate values. If duplicate values are permitted, you can write records containing key values that are present in the key's index. If duplicate values are not permitted, such write operations are rejected.

Indexed organization is possible only on disk devices.

2.2 Record Access Modes

The record access modes are sequential, direct by key, and direct by record file address. The direct access modes are possible only with files that reside on disks.

Unlike a file's organization, the record access mode is not a permanent attribute of the file. During the processing of a file, you can switch from one access mode to any other permitted for that file organization. For example, indexed files are often processed by locating a record directly by key, and then using that key's index to sequentially read all the indexed records in ascending order of their key values; this method is sometimes called the indexed-sequential access method (ISAM).

2.3 RMS Record Formats

Records in RMS files can have the following formats:

  • Fixed-length format, where the length of every record is defined at the time of the file's creation. This format is permitted with any file organization.
  • Variable-length format, where the maximum length of every record is defined at the time of the file's creation. This format is permitted with any file organization.
  • Variable-length format with a fixed-length control area (VFC), where every record is prefixed by a fixed-length field. This format is permitted only with sequential and relative files.
  • Stream format, where records are delimited by special characters called terminators. Terminators are part of the record they delimit. The three types of stream formatting are as follows:
    • Stream, where records can be delimited with a form feed, vertical tab, new-line character, or carriage-return/new-line character.
    • Stream_cr, where records are delimited with the carriage-return character.
    • Stream_lf, where records are delimited with the line-feed character. This format variation is the default format when you create files using the Standard I/O functions.


Previous Next Contents Index
Buy Online or Call 1.800.888.0220      privacy statement and legal notices 
STORES CONTACT US SEARCH PRODUCTS SOLUTIONS OPTIONS DEVELOPERS