Implementation-defined Behavior

The C and/or C++ standards define certain compiler behavior that is implementation defined.

The C and/or C++ standards define certain compiler behavior that is implementation defined. The standards require that the behavior of each particular implementation be documented, and define implementation-defined behavior as behavior that is dependent on the characteristics of the implementation for a correct program construct and correct data.

Messages

All diagnostic messages issued by the compilers are reported through the Cray Linux Environment (CLE) message system. For information about messages issued by the compilers and for information about the Cray Linux Environment (CLE) message system, see Compiler Message System Use.

Environment

When argc and argv are used as parameters to the main function, the array members argv[0] through argv[argc-1] contain pointers to strings that are set by the command shell. The shell sets these arguments to the list of words on the command line used to invoke the compiler (the argument list). For further information about how the words in the argument list are formed, refer to the documentation on the shell being run.

A third parameter, char **envp, provides access to environment variables. The value of the parameter is a pointer to the first element of an array of null-terminated strings that matches the output of the env command. The array of pointers is terminated by a null pointer.

The compiler does not distinguish between interactive devices and other, noninteractive devices. The library, however, may determine that stdin, stdout, and stderr (cin, cout, and cerr in Cray C++) refer to interactive devices and buffer them accordingly.

Identifiers

The identifier (as defined by the standards) is merely a sequence of letters and digits. Specific uses of identifiers are called names.

The Cray C compiler treats the first 255 characters of a name as significant, regardless of whether it is an internal or external name. The case of names, including external names, is significant. In Cray C++, all characters of a name are significant.

Types

The table below summarizes Cray C and C++ types and the characteristics of each type. Representation is the number of bits used to represent an object of that type. Memory is the number of storage bits that an object of that type occupies.

In the Cray C and C++ compilers, size, in the context of the sizeof operator, refers to the size allocated to store the operand in memory; it does not refer to representation, as specified in the table. Thus, the sizeof operator will return a size that is equal to the value in the Memory column of the table divided by 8 (the number of bits in a byte).

Table 1. Data Type Mapping
TypeRepresentation Size and Memory Storage Size (bits)
bool (C++)8
_Bool (C)8
char8
wchar_t32
short16
int32
long64
long long64
float32
double64
long double64
float complex64 (each part is 32 bits)
double complex128 (each part is 64 bits)
long double complex128 (each part is 64 bits)
_float128128
_float128 complex256 (each part is 128 bits)
Pointers64
Vectorization of 8- and 16-bit data types is deferred.

Characters

The full 8-bit ASCII code set can be used in source files. Characters not in the character set defined in the standard are permitted only within character constants, string literals, and comments. The -h [no]calchars option allows the use of the $ sign in identifier names. For more information about the -h [no]calchars option, see -h [no]calchars.

A character consists of 8 bits. Up to 8 characters can be packed into a 64-bit word. A plain char type (that is, one that is declared without a signed or unsigned keyword) is treated as a signed type.

Character constants and string literals can contain any characters defined in the 8-bit ASCII code set. The characters are represented in their full 8-bit form. A character constant can contain up to 8 characters. The integer value of a character constant is the value of the characters packed into a word from left to right, with the result right-justified, as shown in the following table:
Table 2. Packed Characters
Character CountInteger Value
'a'0x61
'ab'0x6162
In a character constant or string literal, if an escape sequence is not recognized, the \ character that initiates the escape sequence is ignored, as shown in the following table:
Table 3. Unrecognizable Escape Sequences
Character ConstantInteger ValueExplanation
'\a'0x7Recognized as the ASCII BEL character
'\8'0x38Not recognized; ASCII value for 8
'\['0x5bNot recognized; ASCII value for [
'\c'0x63Not recognized; ASCII value for c

Wide Characters

Wide characters are treated as signed 64-bit integer types. Wide character constants cannot contain more than one multibyte character. Multibyte characters in wide character constants and wide string literals are converted to wide characters in the compiler by calling the mbtowc() function. The current locale in effect at the time of compilation determines the method by which mbtowc() converts multibyte characters to wide characters, and the shift states required for the encoding of multibyte characters in the source code. If a wide character, as converted from a multibyte character or as specified by an escape sequence, cannot be represented in the extended execution character set, it is truncated.

Integers

All integral values are represented in a two's complement format. When an integer is converted to a shorter signed integer, and the value cannot be represented, the result is the truncated representation treated as a signed quantity. When an unsigned integer is converted to a signed integer of equal length, and the value cannot be represented, the result is the original representation treated as a signed quantity.

The bitwise operators (unary operator ~ and binary operators <<, >>, &, ^, and |) operate on signed integers in the same manner in which they operate on unsigned integers. The result of e1 >> e2, where e1 is a negative-valued signed integral value, is that e1 is right-shifted e2 bit positions; vacated bits are filled with 1s. This behavior can be modified by using the -h nosignedshifts option (see -h nosignedshifts). Bits higher than the sixth bit are not ignored.

The result of the / operator is the largest integer less than or equal to the algebraic quotient when either operand is negative and the result is a nonnegative value. If the result is a negative value, it is the smallest integer greater than or equal to the algebraic quotient. The / operator behaves the same way in C and C++ as in Fortran.

The sign of the result of the percent (%) operator is the sign of the first operand.

Integer overflow is ignored. Because some integer arithmetic uses the floating-point instructions, floating-point overflow can occur during integer operations. Division by 0 and all floating-point exceptions, if not detected as an error by the compiler, can cause a run time abort.

128-Bit Floating Point and 256-Bit Complex Predefined Types

The Cray C and C++ Compilers now support 128-bit floating point and 256-bit complex predefined types using the X86-64 ABI definitions for type names and data layout. These types are sometimes referred to as quad-precision. In C and C++, use __float128, and __float128 _complex. The header file quadmath.h defines the 128-bit functions and the header file complex.h defines the complex functions.

The base type itself uses 128 bits of storage with a guaranteed minimum alignment on a 128-bit boundary, little endian, has a 15-bit exponent, a 113-bit mantissa, and an exponent bias of 16383, and is compatible with the gcc implementation.

In C and C++, long double remains identical to double – 64-bit IEEE, and not 80-bit extended precision.

C forms of intrinsic math functions offer full support for quad-precision types. See the intro_quad_precision(3i) man page for a complete list of intrinsic functions that support quad-precision.

There is no printf descriptor for __float128. quadmath.h defines gnu functions quadmath_snprintf() and strtoflt128() that convert __float128 to strings:
extern __float128 strtoflt128 (const char *, char **);
extern int quadmath_snprintf (char *str, size_t size, const char *format, ...)
Alternatively, use a Fortran subroutine to print a 128-bit floating point number:
% cat printf128main.c printf128.f
#include <quadmath.h>
#include <math.h>
printf128_(__float128 *x);
void main(){
      __float128 x=1.234567890123456789012345678901234567890Q;
      printf128_(&x);
}

      subroutine printf128(qx)
      real*16 qx
      print "('qx:', f5.1, e45.35)",qx,qx
      end

%cc printf128main.c printf128.o && ./a.out
qx: 1.2 0.12345678901234567890123456789012346E+01

Arrays and Pointers

An unsigned long value can hold the maximum size of an array. The type size_t is defined to be a typedef name for unsigned long in the headers: malloc.h, stddef.h, stdio.h, stdlib.h, string.h, and time.h. If more than one of these headers is included, only the first defines size_t.

A type long can hold the difference between two pointers to elements of the same array. The type ptrdiff_t is defined to be a typedef name for long in the header stddef.h.

If a pointer type's value is cast to a signed or unsigned long int, and then cast back to the original type's value, the two pointer values will compare equal. Type-casting from pointer to long int enables pointer arithmetic. For example:
static void **Table;
size_t offset = -BlockSize[nr];
Table = (void **) malloc(MAXBLOCKS * sizeof(void *));
Table[i] = (void **) (((long) Table[i]) + offset);

Pointers on Cray Linux Environment (CLE) systems are byte pointers. Byte pointers use the same internal representation as integers; a byte pointer counts the numbers of bytes from the first address.

A pointer can be explicitly converted to any integral type large enough to hold it. The result will have the same bit pattern as the original pointer. Similarly, any value of integral type can be explicitly converted to a pointer. The resulting pointer will have the same bit pattern as the original integral type.

Registers

Use of the register storage class in the declaration of an object has no effect on whether the object is placed in a register. The compiler performs register assignment aggressively; that is, it automatically attempts to place as many variables as possible into registers.

Classes, Structures, Unions, Enumerations, and Bit Fields

Accessing a member of a union by using a member of a different type results in an attempt to interpret, without conversion, the representation of the value of the member as the representation of a value in the different type.

Members of a class or structure are packed into words from left to right. Padding is appended to a member to correctly align the following member, if necessary. Member alignment is based on the size of the member:
  • For a member bit field of any size, alignment is any bit position that allows the member to fit entirely within a 64–bit word.
  • For a member with a size less than 64 bits, alignment is the same as the size. For example, a char has a size and alignment of 8 bits; a float has a size and alignment of 32 bits.
  • For a member with a size equal to or greater than 64 bits, alignment is 64 bits.
  • For a member with array type, alignment is equal to the alignment of the element type.

A plain int type bit field is treated as a signed int bit field.

The values of an enumeration type are represented in the type signed int in C; they are a separate type in C++.

Qualifiers

When an object that has volatile-qualified type is accessed, it is simply a reference to the value of the object. If the value is not used, the reference need not result in a load of the value from memory.

Declarators

A maximum of 12 pointer, array, and/or function declarators are allowed to modify an arithmetic, structure, or union type.

Statements

The compiler has no fixed limit on the maximum number of case values allowed in a switch statement. The Cray C++ compiler parses asm statements for correct syntax, but otherwise ignores them.

Exceptions

In Cray C++, when an exception is thrown, the memory for the temporary copy of the exception being thrown is allocated on the stack and a pointer to the allocated space is returned.

System Function Calls

For a description of the form of the unsuccessful termination status that is returned from a call to exit(3), see the exit(3) man page.

Preprocessing

The value of a single-character constant in a constant expression that controls conditional inclusion matches the value of the same character in the execution character set. No such character constant has a negative value. For each, 'a' has the same value in the two contexts:
#if 'a' == 97 
if ('a' == 97)
The -I option and the method for locating included source files is described in -I incldir.
The source file character sequence in an #include directive must be a valid or Cray Linux Environment (CLE) file name or path name. An #include directive may specify a file name by means of a macro, provided the macro expands into a source file character sequence delimited by double quotes or < and > delimiters, as follows:

#define myheader "./myheader.h" 
#include myheader 
#define STDIO <stdio.h> 
#include STDIO
The macros __DATE__ and __TIME__ contain the date and time of the beginning of translation.