Compaq C
Compaq C User's Guide for OpenVMS Systems
 
 
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:
 
  - Saves the current state of the
member_alignment
,
extern_model
,
extern_prefix
, and
message
 pragmas.
  
 - 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:
 
  - 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.
   - 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.
  
 - The text of the prologue file is processed just before the 
  text of the file specified by the
#include
.
  
 - The text of the epilogue file is processed just after the 
  text of the file specified by the #include.
  
 - 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.
  
  
  
  
        
	
	  | 
	 
		 
		
		
	 
	
	
 
 |