Compaq C
Compaq C Run-Time Library Reference Manual for
OpenVMS Systems
1.8.2.2.1 Accessing Variable-Length or VFC Record Files in Record Mode
When you access a variable-length or VFC record file in record mode,
many I/O functions behave differently than they would if they were
being used with stream mode. This section describes these differences.
In general, the new-line character (\n) is the record separator for all
record modes. On output, when a new-line character is encountered, a
record is generated unless you specify an optional argument (such as
"ctx=bin" or "ctx=xplct") that affects the interpretation of new lines.
The
read
and
decc$record_read
functions always read at most one record. The
write
and
decc$record_write
functions always generate at least one record.
decc$record_read
and
decc$record_write
are equivalent, respectively, to
read
and
write
, except that they work with file pointers rather than file descriptors.
Unlike the
read
function which reads at most one record, the
fread
function can span records. Rather than read number_items
records (where number_items is the third parameter to
fread
),
fread
tries to read the number of bytes equal to number_items x
size_of_item (where size_of_item is the second
parameter to
fread
). The value returned by
fread
is equal to the number of bytes read divided by size_of_item.
However, the
fwrite
function always generates at least number_items records.
The
fgets
and
gets
functions read to either a new-line character or a record boundary.
The
fflush
function always generates a record if there is unwritten data in the
buffer. The same is true of
close
,
fclose
,
fseek
,
lseek
,
rewind
, and
fsetpos
, all of which perform implicit
fflush
functions.
A record is also generated whenever an attempt is made to write more
characters than allowed by the maximum record size.
For more information on these functions, see the Reference Section.
1.8.2.2.2 Accessing Fixed-Length Record Files in Record Mode
When accessing a fixed-length record file in record mode, the I/O
functions generally behave as described in Section 1.8.2.2.1.
The
write
,
fwrite
, and
decc$record_write
functions will fail if given a record size that is not an integral
multiple of the maximum record size, unless the file was opened with
the "ctx=xplct" optional argument specified. All other output functions
will generate records at every nth byte, where n is
the maximum record size.
If a new record is forced by
fflush
, the data in the buffer is padded to the maximum record size with null
characters.
Note
This padding can cause problems for programs that seek to the
end-of-file. For example, if a program were to append data to a file,
then seek backwards in the file (causing an
fflush
to occur), and then seek to the end-of-file again, a zero-filled "hole"
will have been created between the previous end-of-file and the new
end-of-file if the previous end-of-file was not on a record boundary.
|
1.8.2.3 Example---Difference Between Stream Mode and Record Mode
Example 1-1 demonstrates the difference between stream mode and
record mode access.
Example 1-1 Differences Between Stream Mode
and Record Mode |
/* CHAP_1_STREAM_RECORD.C */
/* This program demonstrates the difference between */
/* record mode and stream mode Input/Output. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void process_records(const char *fspec, FILE * fp);
main()
{
FILE *fp;
fp = fopen("example-fixed.dat", "w", "rfm=fix", "mrs=40", "rat=none");
if (fp == NULL) {
perror("example-fixed");
exit(EXIT_FAILURE);
}
printf("Record mode\n");
process_records("example-fixed.dat", fp);
fclose(fp);
printf("\nStream mode\n");
fp = fopen("example-streamlf.dat", "w");
if (fp == NULL) {
perror("example-streamlf");
exit(EXIT_FAILURE);
}
process_records("example-streamlf.dat", fp);
fclose(fp);
}
void process_records(const char *fspec, FILE * fp)
{
int i,
sts;
char buffer[40];
/* Write records of all 1's, all 2's and all 3's */
for (i = 0; i < 3; i++) {
memset(buffer, '1' + i, 40);
sts = fwrite(buffer, 40, 1, fp);
if (sts != 1) {
perror("fwrite");
exit(EXIT_FAILURE);
}
}
/* Rewind the file and write 10 characters of A's, then 10 B's, */
/* then 10 C's. */
/* */
/* For stream mode, each fwrite call outputs 10 characters */
/* and advances the file position 10 characters */
/* characters. */
/* */
/* For record mode, each fwrite merges the 10 characters into */
/* the existing 40-character record, updates the record and */
/* advances the file position 40 characters to the next record. */
rewind(fp);
for (i = 0; i < 3; i++) {
memset(buffer, 'A' + i, 10);
sts = fwrite(buffer, 10, 1, fp);
if (sts != 1) {
perror("fwrite2");
exit(EXIT_FAILURE);
}
}
/* Now reopen the file and output the records. */
fclose(fp);
fp = fopen(fspec, "r");
for (i = 0; i < 3; i++) {
sts = fread(buffer, 40, 1, fp);
if (sts != 1)
perror("fread");
printf("%.40s\n", buffer);
}
return;
}
|
Running this program produces the following output:
Record Mode
AAAAAAAAAA111111111111111111111111111111
BBBBBBBBBB222222222222222222222222222222
CCCCCCCCCC333333333333333333333333333333
Stream mode
AAAAAAAAAABBBBBBBBBBCCCCCCCCCC1111111111
2222222222222222222222222222222222222222
3333333333333333333333333333333333333333
|
1.9 Specific Portability Concerns
One of the last tasks in preparing to use the Compaq C RTL, if you are
going to port your source programs across systems, is to be aware of
specific differences between the Compaq C RTL and the run-time
libraries of other implementations of the C language. This section
describes some of the problems that you might encounter when porting
programs to and from an OpenVMS system. Although portability
is closely tied to the implementation of the Compaq C RTL, this
section also contains information on the portability of other
Compaq C for OpenVMS constructs.
The Compaq C RTL provides ANSI C defined library functions as well
as many commonly available APIs and a few OpenVMS extensions.
See Section 1.5 for specific standards, portions of which are
implemented by the Compaq C RTL. Attempts have been made to
maintain complete portability in functionality whenever possible. Many
of the Standard I/O and UNIX I/O functions and macros contained in the
Compaq C RTL are functionally equivalent to those of other
implementations.
The RTL function and macro descriptions elaborate on issues presented
in this section and describe concerns not documented here.
The following list documents issues of concern if you wish to port C
programs to the OpenVMS environment:
- Compaq C for OpenVMS Systems does not implement the global symbols
end
,
edata
, and
etext
.
- There are differences in how OpenVMS and UNIX systems lay
out virtual memory. In some UNIX systems, the address space between 0
and the break address is accessible to your program. In
OpenVMS systems, the first page of memory is not accessible.
For example, if a program tries to reference location 0 on an
OpenVMS system, a hardware error
(ACCVIO) is returned and the program terminates abnormally.
OpenVMS systems reserve the first page of address space to
catch incorrect pointer references, such as a reference to a location
pointed to by a null pointer. For this reason, some existing programs
that run on some UNIX systems may fail and you should modify them, as
necessary. (Tru64 UNIX and OpenVMS, however, are
compatible in this regard.)
- Some C programmers code all external declarations in
#include
files. Then, specific declarations that require initialization are
redeclared in the relevant module. This practice causes the
Compaq C compiler to issue a warning message about multiply
declared variables in the same compilation. One way to avoid this
warning is to make the redeclared symbols
extern
variables in the
#include
files.
- Compaq C does not support
asm
calls on OpenVMS VAX systems. They are supported on
OpenVMS Alpha systems. See the Compaq C User's Guide for OpenVMS Systems for more
information on intrinsic functions.
- Some C programs call the counted string functions
strcmpn
and
strcpyn
. These names are not used by Compaq C for OpenVMS Systems. Instead, you can define
macros that expand the
strcmpn
and
strcpyn
names into the equivalent, ANSI-compliant names
strncmp
and
strncpy
.
- The Compaq C for OpenVMS compiler does not support the following
initialization form:
Programs using this form of initialization must be changed.
- Compaq C for OpenVMS Systems predefines several compile-time macros such as
__vax
,
__alpha
,
__32BITS
,
__vms
,
__vaxc
,
__VMS_VER
,
__DECC_VER
,
__D_FLOAT
,
__G_FLOAT
,
__IEEE_FLOAT
,
__X_FLOAT
, and others. These predefined macros are useful for programs that must
be compatible on other machines and operating systems. For more
information, see the predefined macro chapter of the Compaq C User's Guide for OpenVMS Systems.
- The ANSI C language does not guarantee any memory order for the
variables in a declaration. For example:
- Depending on the type of external linkage requested,
extern
variables in a program may be treated differently using Compaq C
on OpenVMS systems than they would on UNIX systems. See the
Compaq C User's Guide for OpenVMS Systems for more information.
- The dollar sign ($) is a legal character in Compaq C for OpenVMS
identifiers, and can be used as the first character.
- The ANSI C language does not define any order for evaluating
expressions in function parameter lists or for many kinds of
expressions. The way in which different C compilers evaluate an
expression is only important when the expression has side effects.
Consider the following examples:
Neither Compaq C nor any other C compiler can guarantee that
such expressions evaluate in the same order on all C compilers.
- The size of a Compaq C variable of type
int
is 32 bits on OpenVMS systems. You will have to modify
programs that are written for other machines and that assume a
different size for a variable of type
int
. A variable of type
long
is the same size (32 bits) as a variable of type
int
.
- The C language defines structure alignment to be dependent on the
machine for which the compiler is designed. On OpenVMS VAX
systems, Compaq C aligns structure members on byte boundaries,
unless
#pragma member_alignment
is specified. On OpenVMS Alpha systems, Compaq C aligns
structure members on natural boundaries, unless
#pragma nomember_alignment
is specified. Other implementations may align structure members
differently.
- References to structure members in Compaq C cannot be vague.
For more information, see the Compaq C Language Reference Manual.
- Registers are allocated based upon how often a variable is used,
but the
register
keyword gives the compiler a strong hint that you want to place a
particular variable into a register. Whenever possible, the variable is
placed into a register. Any scalar variable with the storage class
auto
or
register
can be allocated to a register as long as the variable's address is not
taken with the ampersand operator (&) and it is not a member of a
structure or union.
1.9.1 Reentrancy
The Compaq C RTL supports an improved and enhanced reentrancy. The
following types of reentrancy are supported:
- AST reentrancy uses the _BBSSI built-in function to perform simple
locking
around critical sections of RTL code, but it may also disable
asynchronous system traps (AST)s in locked regions of code. This type
of locking should be used when AST code contains calls to Compaq C RTL
I/O routines.
Failure to specify AST reentrancy might cause I/O
routines to fail, setting
errno
to EALREADY.
- MULTITHREAD reentrancy is designed to be used in threaded
programs such as those that use the DECthreads library. It performs
DECthreads locking and never disables ASTs. DECthreads must be
available on your system to use this form of reentrancy.
- TOLERANT reentrancy uses the _BBSSI built-in function to perform
simple locking
around critical sections of RTL code, but ASTs are not disabled. This
type of locking should be used when ASTs are used and must be delivered
immediately.
- NONE gives optimal performance in the Compaq C RTL, but does
absolutely no locking around critical sections of RTL code. It should
only be used in a single-threaded environment when there is no chance
that the thread of execution will be interrupted by an AST that would
call the Compaq C RTL.
The default reentrancy type is TOLERANT.
You can set the reentrancy type by compiling with the /REENTRANCY
command-line qualifier or by calling the
decc$set_reentrancy
function.
This function must be called exclusively from non-AST level.
When programming an application using multiple threads or ASTs, you
should consider three classes of functions:
- Functions with no internal data
- Functions with thread-local internal data
- Functions with process-wide internal data
Most functions have no internal data at all. For these functions,
synchronization is necessary only if the parameter is used by the
application in multiple threads or in both AST and non-AST context. For
example, although the
strcat
function is ordinarily safe, the following is an example of unsafe
usage:
extern char buffer[100];
void routine1(char *data) {
strcat( buffer, data );
}
|
If
routine1
executed concurrently in multiple threads, or if
routine1
is interrupted by an AST routine that calls it, the results of the
strcat
call are unpredictable.
The second class of functions are those that have thread-local static
data. Typically, these are routines in the library that return a string
where the application is not permitted to free the storage for the
string. These routines are thread-safe but not AST-reentrant. This
means they can safely be called concurrently, and each thread will have
its own copy of the data. They cannot be called from AST routines if it
is possible that the same routine was executing in non-AST context. The
routines in this class are:
asctime stat
ctermid strerror
ctime strtok
cuserid VAXC$ESTABLISH
gmtime the errno variable
localtime wcstok
perror
|
All the socket functions are also included in this list if the TCP/IP
product in use is thread-safe.
The third class of functions are those that affect process-wide data.
These functions are neither thread-safe nor AST-reentrant. For example,
sigsetmask
establishes the process-wide signal mask. Consider a routine like the
following:
void update_data
base()
{
int old_mask;
old_mask = sigsetmask( 1 << (SIGINT - 1));
/* Do work here that should not be aborted. */
sigsetmask( old_mask );
}
|
If
update_database
was called concurrently in multiple threads, thread 1 might unblock
SIGINT while thread 2 was still performing work that should not be
aborted.
The routines in this class are:
- All the
signal
routines
- All the
exec
routines
- The
exit
,
_exit
,
nice
,
system
,
wait
,
getitimer
,
setitimer
, and
setlocale
routines.
Note
Generally speaking, UTC-based time functions can affect in-memory
time-zone information, which is process-wide data. However, if the
system time zone remains the same during the execution of the
application (which is the common case) and the cache of timezone files
is enabled (which is the default), then the
_r
variant of the time functions
asctime_r
,
ctime_r
,
gmtime_r
and
localtime_r
is both thread-safe and AST-reentrant.
If, however, the system time zone can change during the execution of
the application or the cache of timezone files is not enabled, then
both variants of the UTC-based time functions belong to the third class
of functions, which are neither thread-safe nor AST-reentrant.
|
1.9.2 Multithread Restrictions
Mixing the multithread programming model and the OpenVMS AST
programming model in the same application is not recommended. The
application has no mechanism to control which thread gets interrupted
by an AST. This can result in a resource deadlock if the thread holds a
resource that is also needed by the AST routine. The following routines
use mutexes. To avoid a potential resource deadlock, do not call them
from AST routines in a multithreaded application.
- All the I/O routines
- All the socket routines
- All the signal routines
-
vfork
,
exec
,
wait
,
system
-
catgets
-
set_new_handler
(C++ only)
-
getenv
-
rand
and
srand
-
exit
and
_exit
-
clock
-
nice
-
times
-
ctime
,
localtime
,
asctime
,
mktime
1.10 64-bit Pointer Support (ALPHA ONLY)
This section is for application developers who need to use 64-bit
virtual memory addressing on OpenVMS Alpha Version 7.0 or
higher.
OpenVMS Alpha 64-bit virtual addressing support makes the
64-bit virtual address space defined by the Alpha architecture
available to both the OpenVMS operating system and its users.
It also allows per-process virtual addressing for accessing dynamically
mapped data beyond traditional 32-bit limits.
The Compaq C Run-Time Library on OpenVMS Alpha Version
7.0 systems and higher includes the following features in support of
64-bit pointers:
- Guaranteed binary and source compatibility of existing programs
- No impact on applications that are not modified to exploit 64-bit
support
- Enhanced memory allocation routines that allocate 64-bit memory
- Widened function parameters to accommodate 64-bit pointers
- Dual implementations of functions that need to know the pointer
size used by the caller
- New information available to the DEC C Version 5.2 compiler or
higher to seamlessly call the correct implementation
- Ability to explicitly call either the 32-bit or 64-bit form of
functions for applications that mix pointer sizes
- A single shareable image for use by 32-bit and 64-bit applications
1.10.1 Using the Compaq C Run-Time Library
The Compaq C Run-Time library on OpenVMS Alpha Version
7.0 systems and higher can generate and accept 64-bit pointers.
Functions that require a second interface to be used with 64-bit
pointers reside in the same object libraries and shareable images as
their 32-bit counterparts. No new object libraries or shareable images
are introduced. Using 64-bit pointers does not require changes to your
link command or link options files.
The Compaq C 64-bit environment allows an application to use both
32-bit and 64-bit addresses. For more information about how to
manipulate pointer sizes, see the /POINTER_SIZE qualifier and
#pragma pointer_size
and
#pragma required_pointer_size
preprocessor directives in the Compaq C User's Guide for OpenVMS Systems.
The /POINTER_SIZE qualifier requires you to specify a value of 32 or
64. This value is used as the default pointer size within the
compilation unit. As an application programmer, you can compile one set
of modules by using 32-bit pointers and another set by using 64-bit
pointers. Care must be taken when these two separate groups of modules
call each other.
Use of the /POINTER_SIZE qualifier also influences the processing of
Compaq C RTL header files. For those functions that have a 32-bit
and 64-bit implementation, specifying /POINTER_SIZE enables function
prototypes to access both functions, regardless of the actual value
supplied to the qualifier. In addition, the value specified to the
qualifier determines the default implementation to call during that
compilation unit.
The
#pragma pointer_size
and
#pragma required_pointer_size
preprocessor directives can be used to change the pointer size in
effect within a compilation unit. You can default pointers to 32-bit
pointers and then declare specific pointers within the module as 64-bit
pointers. You would also need to specifically call the
_malloc64
form of
malloc
to obtain memory from the 64-bit memory area.
|
|