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.
|
|