This appendix discusses some of the features of the ISO/IEC 9899:1999, Programming Language - C standard.
The -std=c99 or -std=gnu99 flag controls compiler recognition of 9899:1999 ISO/C. For more information about the syntax of the -std flag, see -std=value.
This appendix provides discussions and examples for some of the following supported features:
Sub-clause 5.2.4.2.2 Characteristics of floating types <float.h>
Sub-clause 6.2.5 _Bool
Sub-clause 6.2.5 _Complex type
Sub-clause 6.3.2.1 Conversion of arrays to pointers not limited to lvalues
Sub-clause 6.4.1 Keywords
Sub-clause 6.4.2.2 Predefined identifiers
6.4.3 Universal character names
Sub-clause 6.4.4.2 Hexadecimal floating-point literals
Sub-clause 6.4.9 Comments
Sub-clause 6.5.2.2 Function calls
Sub-clause 6.5.2.5 Compound literals
Sub-clause 6.7.2 Type specifiers
Sub-clause 6.7.2.1 Structure and union specifiers
Sub-clause 6.7.3 Type Qualifier
Sub-clause 6.7.4 Function specifiers
Sub-clause 6.7.5.2 Array declarator
Sub-clause 6.7.8 Initialization
Sub-clause 6.8.2 Compound statement
Sub-clause 6.8.5 Iteration statements
Sub-clause 6.10.3 Macro replacement
Sub-clause 6.10.6 STDC pragmas
Sub-clause 6.10.8 __STDC_IEC_559 and __STDC_IEC_559_COMPLEX macros
Sub-clause 6.10.9 Pragma operator
Feature: 5.2.4.2.2 Characteristics of floating types <float.h>
The values of operations with floating operands, and the values that are subject to both the usual arithmetic conversions and to floating constants, are evaluated to a format whose range and precision may be greater than required by the type. The use of evaluation formats is characterized by the implementation-defined value of FLT_EVAL_METHOD, shown in the following table.
Table 83 FLT_EVAL_METHOD Values
|
When you include float.h, FLT_EVAL_METHOD expands to 0 by default and all floating-point expressions are evaluated according to their type.
The -Xt option does not affect the expansion of FLT_EVAL_METHOD, even though float expressions are evaluated as double. See -X[c|a|t|s] for more information.
The -fsingle option causes float expressions to be evaluated with single precision. See -fsingle for more information.
When you specify -fprecision on x86 architectures with -xarch=sse2 (or any later version of the SSE2 processor family such as sse3, ssse3, sse4_1, sse4_2, and so on) or -m64 and include float.h, FLT_EVAL_METHOD expands to -1.
The C99 standard introduces the following new keywords. The compiler issues a warning if you use these keywords as identifiers while compiling with -std=c89, -std=c90, -std=gnu89, or -std=gnu90. With -std=c99, -std=gnu99, -std=c11 or -std=gnu11 the compiler issues a warning or error messages for use of these keywords as identifiers depending on the context.
inline
_Imaginary
_Complex
_Bool
restrict
An object that is accessed through a restrict qualified pointer requires that all accesses to that object use, directly or indirectly, the value of that particular restrict qualified pointer. Any access to the object through any other means might result in undefined behavior. The intended use of the restrict qualifier is to enable the compiler to make assumptions that promote optimizations.
See Restricted Pointers for examples and an explanation about how to use the restrict qualifier effectively.
Feature: 6.4.2.2 Predefined identifiers
The compiler provides support for the predefined identifier __func__, defined as an array of chars which contains the name of the current function in which __func__ appears.
Feature: 6.4.3 Universal character names
UCN allows the use of any character in a C source, not just English characters. A UCN has the following format:
\u4_hex_digits_value
\U8_hex_digits_value
A UCN must not specify a value less than 00A0 other than 0024 ($), 0040 (@), or 0060 (?), nor a value in the range D800 through DFFF inclusive.
UCN may be used in identifiers, character constants, and string literals to designate characters that are not in the C basic character set.
The UCN \Unnnnnnnn designates the character whose eight-digit short identifier (as specified by ISO/IEC 10646) is nnnnnnnn. Similarly, the universal character name \unnnn designates the character whose four-digit short identifier is nnnn (and whose eight-digit short identifier is 0000nnnn).
The characters // introduce a comment that includes all multibyte characters up to, but not including, the next new-line character except when the // characters appear within a character constant, a string literal, or a comment.
Feature: 6.5.2.2 Function calls
Implicit declarations are no longer allowed in the 1999 C standard as they were in the 1990 C standard. Previous versions of the C compiler issued warning messages about implicit definitions only with -v (verbose). These messages and new additional warnings about implicit definitions are now issued whenever identifiers are implicitly defined as int or functions.
This change is very likely to be noticed by nearly all users of this compiler because it can lead to a large number of warning messages. Common causes include a failure to include the appropriate system header files that declare functions being used, for example,printf, which needs <stdio.h> included. The 1990 C standard behavior of accepting implicit declarations silently can be restored using -std=c89, -std=c90, -std=gnu89, or -std=gnu90.
The C compiler now generates a warning for an implicit function declaration, as shown in the following example.
example% cat test.c void main() { printf("Hello, world!\n"); } example% cc test.c "test.c", line 3: warning: implicit function declaration: printf example%
Feature: 6.7.2 Type specifiers:
At least one type specifier shall be given in the declaration specifiers in each declaration. For more information, see Disallowed Implicit int and Implicit Function Declarations.
The C compiler now issues warnings on any implicit int declaration as shown in the following example:
example% more test.c volatile i; const foo() { return i; } example% cc test.c "test.c", line 1: warning: no explicit type given "test.c", line 3: warning: no explicit type given example%
Feature: 6.7.2.1 Structure and union specifiers
This feature is also known as the struct hack. Allows the last member of a struct to be an array of zero length, such as int foo[]; This type of a struct is commonly used as the header to access malloc()'d memory.
For example, in this structure, struct s { int n; double d[]; } S;, the array, d, is an incomplete array type. The C compiler does not count any memory offset for this member of S. In other words, sizeof(struct s) is the same as the offset of S.n.
d can be used like any ordinary array-member for example, S.d[10] = 0;.
Without the C compiler’s support for an incomplete array type, you would define and declare a structure as the following example, called DynamicDouble, shows:
typedef struct { int n; double d[1]; ) DynamicDouble;
Note that the array d is not an incomplete array type and is declared with one member.
Next, you declare a pointer dd and allocate memory:
DynamicDouble *dd = malloc(sizeof(DynamicDouble)+(actual_size-1)*sizeof(double));
You then store the size of the offset in S.n thus:
dd->n = actual_size;
Because the compiler supports incomplete array types, you can achieve the same result without declaring the array with one member:
typedef struct { int n; double d[]; } DynamicDouble;
You now declare a pointer dd and allocate memory as before, except that you no longer have to subtract one from actual_size:
DynamicDouble *dd = malloc (sizeof(DynamicDouble) + (actual_size)*sizeof(double));
The offset is stored, as before, in S.n :
dd->n = actual_size;
Feature: 6.7.3 Type qualifiers
If the same qualifier appears more than once in the same specifier-qualifier-list, either directly or through one or more typedefs, the behavior is the same as when the type qualifier appears only once.
In C90, the following code would cause an error:
%example cat test.c const const int a; int main(void) { return(0); } %example cc -std=c89 test.c "test.c", line 1: invalid type combination
However, with C99, the C compiler accepts multiple qualifiers.
%example cc -std=c99 test.c %example
Feature: 6.7.4 Function specifiers
Inline functions as defined by the 1999 C ISO standard are fully supported.
Note that according to the C standard, inline is only a suggestion to the C compiler. The C compiler can choose not to inline anything, and compile calls to the actual function.
The Oracle Developer Studio C compiler does not inline C function calls unless compiling at optimization level -xO3 or above, and only if the optimizer's heuristics determine that it is profitable to do so. The C compiler does not provide a way to force a function to be inlined.
Static inline functions are simple. Either a function defined with the inline function specifier is inlined at a reference, or a call is made to the actual function. The compiler can choose which to do at each reference. The compiler determines whether it is profitable to inline at -xO3 and above. If not profitable to inline (or at an optimization of less than -xO3), a reference to the actual function will be compiled and the function definition will be compiled into the object code. Note that if the program uses the address of the function, the actual function will be compiled in the object code and not inlined.
Extern inline functions are more complicated. Two types of extern inline functions are: an inline definition and an extern inline function.
An inline definition is a function defined with the keyword inline, without either the keywords static or extern, and with all prototypes appearing within the source (or included files) also containing the keyword inline without either the keywords static or extern. For an inline definition the compiler must not create a global definition of the function. That means any reference to an inline definition that is not inlined will be a reference to a global function defined elsewhere. Put another way, the object file produced by compiling this translation unit (source file) will not contain a global symbol for the inline definition. And any reference to the function that is not inlined will be to an extern (global) symbol provided by some other object file or library at link time.
An extern inline function is declared by a file scope declaration with the extern storage-class-specifier (that is, the function definition or prototype). For an extern inline function the compiler will provide a global definition of the function in the resulting object file. The compiler may choose to inline any references to that function seen in the translation unit (source file) where the function definition has been provided, or the compiler can choose to call the global function.
The behavior of any program that relies on whether a function call is actually inlined is undefined.
Note also that an inline function with external linkage may not declare or reference a static variable anywhere in the translation-unit.
To obtain behavior from the Oracle Developer Studio C compiler that is compatible with the GNU C compiler's implementation of extern inline functions for most programs, use the -features=no%extinl flag. When this flag is specified the Oracle Developer Studio C compiler will treat the function as if it were declared as a static inline function.
The one place this is not compatible will be when the address of the function is taken. With gcc this will be an address of a global function, and with Oracle Developer Studio's C compiler the local static definition address will be used.
Feature: 6.7.5.2 Array declarator
The keyword static can now appear in the Array declarator of a parameter in a function declarator to indicate that the compiler can assume at least that many elements will be passed to the function being declared. Allows the optimizer to make assumptions about which it otherwise could not determine.
The C compiler adjusts array parameters into pointers therefore void foo(int a[]) is the same as void foo(int *a).
If you specify type qualifiers such as void foo(int * restrict a);, the C compiler expresses it with array syntax void foo(int a[restrict]); which is essentially the same as declaring a restricted pointer.
The C compiler also uses a static qualifier to preserve information about the array size. For example, if you specify void foo(int a[10]) the compiler still expresses it as void foo(int *a). Use a static qualifier as follows, void foo(int a[static 10]), to let the compiler know that pointer a is not NULL and that it provides access to an integer array of at least ten elements.
Feature: 6.7.5.2 Array declarators
VLAs are allocated on the stack as if by calling the alloca function. Their lifetime, regardless of their scope, is the same as any data allocated on the stack by calling alloca; until the function returns. The space allocated is freed when the stack is released upon returning from the function in which the VLA is allocated.
Not all constraints are yet enforced for variable length arrays. Constraint violations lead to undefined results.
#include <stdio.h> void foo(int); int main(void) { foo(4); return(0); } void foo (int n) { int i; int a[n]; for (i = 0; i < n; i++) a[i] = n-i; for (i = n-1; i >= 0; i--) printf("a[%d] = %d\n", i, a[i]); } example% cc test.c example% a.out a[3] = 1 a[2] = 2 a[1] = 3 a[0] = 4
Feature: 6.7.8 Initialization
Designated initializers provide a mechanism for initializing sparse arrays, a practice common in numerical programming.
Designated initializers enable initialization of sparse structures, common in systems programming, and initialization of unions through any member, regardless of whether it is the first member.
Consider these examples. This first example shows how designated initializers are used to initialize an array:
enum { first, second, third }; const char *nm[] = { [third] = "third member", [first] = "first member", [second] = "second member", };
The following example demonstrates how designated initializers are used to initialize the fields of a struct object:
division_t result = { .quot = 2, .rem = -1 };
The following example shows how designated initializers can be used to initialize complicated structures that might otherwise be misunderstood:
struct { int z[3], count; } w[] = { [0].z = {1}, [1].z[0] = 2 };
An array can be created from both ends by using a single designator:
int z[MAX] = {1, 3, 5, 7, 9, [MAX-5] = 8, 6, 4, 2, 0};
If MAX is greater than ten, the array will contain zero-valued elements in the middle; if MAX is less than ten, some of the values provided by the first five initializers will be overridden by the second five.
Any member of a union can be initialized:
union { int i; float f;} data = { .f = 3.2 };
Feature: 6.8.2 Compound statement
The C compiler accepts mixing type declarations with executable code as shown by the following example:
#include <stdio.h> int main(void){ int num1 = 3; printf("%d\n", num1); int num2 = 10; printf("%d\n", num2); return(0); }
Feature: 6.8.5 Iteration statements
The C compiler accepts a type declaration as the first expression in a for loop-statement:
for (int i=0; i<10; i++){ //loop body };
The scope of any variable declared in the initialization statement of the for loop is the entire loop (including controlling and iteration expressions).
Feature: 6.10.3 Macro replacement
The C compiler accepts #define preprocessor directives of the following form:
#define identifier (...) replacement_list #define identifier (identifier_list, ...) replacement_list
If the identifier_list in the macro definition ends with an ellipses, it means that there will be more arguments in the invocation than there are parameters in the macro definition, excluding the ellipsis. Otherwise, the number of parameters in the macro definition, including those arguments that contain no preprocessing tokens, matches the number of arguments. Use the identifier __VA_ARGS__ in the replacement list of a #define preprocessing directive that uses the ellipsis notation in its arguments. The following example demonstrates the variable argument list macro facilities.
#define debug(...) fprintf(stderr, __VA_ARGS__) #define showlist(...) puts(#__VA_ARGS__) #define report(test, ...) ((test)?puts(#test):\ printf(__VA_ARGS__)) debug(“Flag”); debug(“X = %d\n”,x); showlist(The first, second, and third items.); report(x>y, “x is %d but y is %d”, x, y);
which results in the following:
fprintf(stderr, “Flag”); fprintf(stderr, “X = %d\n”, x); puts(“The first, second, and third items.”); ((x>y)?puts(“x>y”):printf(“x is %d but y is %d”, x, y));
Feature: 6.10.9 Pragma operator
A unary operator expression of the form: _Pragma ( string-literal ) is processed as follows:
The L prefix of the string literal is deleted, if it is present.
The leading and trailing double-quotes are deleted.
Each escape sequence ’ is replaced by a double-quote.
Each escape sequence \\ is replaced by a single backslash.
The resulting sequence of preprocessing tokens are processed as if they were the preprocessor tokens in a pragma directive.
The original four preprocessing tokens in the unary operator expression are removed.
_Pragma offers an advantage over #pragma in that _Pragma can be used in a macro definition.
_Pragma("string") behaves exactly the same as #pragma string. Consider the following example. First, the example’s source code is listed and then the example’s source is listed after the preprocessor has made its pass.
example% cat test.c #include <omp.h> #include <stdio.h> #define Pragma(x) _Pragma(#x) #define OMP(directive) Pragma(omp directive) void main() { omp_set_dynamic(0); omp_set_num_threads(2); OMP(parallel) { printf("Hello!\n"); } } example% cc test.c -P -xopenmp -xO3 example% cat test.i
The following shows the source after the preprocessor has finished.
void main() { omp_set_dynamic(0); omp_set_num_threads(2); # pragma omp parallel { printf("Hello!\n"); } } example% cc test.c -xopenmp example% ./a.out Hello! Hello! example%