Sun Studio 12: C User's Guide

D.1 Discussion and Examples

This appendix provides discussions and examples for some of the following supported features:

D.1.1 Precision of Floating Point Evaluators

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:

Table D–1 The FLT_EVAL_METHOD Values

Value  

Meaning  

-1 

Indeterminable. 

The compiler evaluates all operations and constants just to the range and precision of the type. 

The compiler evaluates operations and constants of type float and double to the range and precision of a double. Evaluate long double operations and constants to the range and precision of a long double. 

The compiler evaluates all operations and constants to the range and precision of a long double. 

When you include float.h on SPARC architectures, FLT_EVAL_METHOD expands to 0 by default and all floating point expressions are evaluated according to their type.

When you include float.h on x86 architectures, FLT_EVAL_METHOD expands to -1 by default (except when -xarch=sse2 or -xarch=amd64), and all floating point constant expressions are evaluated according to their type and all other floating point expressions are evaluated as long double.

When you specify -flteval=2 and include float.h, FLT_EVAL_METHOD expands to 2 and all floating expressions are evaluated as long double. See B.2.20 -flteval[={any|2}] for more information.

When you specify -xarch=sse2 or -xarch=amd64 on x86, and include float.h, FLT_EVAL_METHOD expands to 0 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 B.2.63 -X[c|a|t|s] for more information.

The -fsingle option causes float expressions to be evaluated with single precision. See B.2.27 -fsingle for more information.

When you specify -fprecision on x86 architectures with -xarch=sse2 or -xarch=amd64 and include float.h, FLT_EVAL_METHOD expands to -1.

D.1.2 C99 Keywords

6.4.1 Keywords

The C99 standard introduces the following new keywords. The compiler issues a warning if you use these keywords as identifiers while compiling with -xc99=none. Without -xc99=none the compiler issues a warning or error messages for use of these keywords as identifiers depending on the context.

D.1.2.1 Using the restrict Keyword

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 may result in undefined behavior. The intended use of the restrict qualifier is to allow the compiler to make assumptions that promote optimizations.

See 3.8.2 Restricted Pointers for examples and an explanation on how to use the restrict qualifier effectively.

D.1.3 __func__ Support

6.4.2.2 Predefined identifiers

The compiler provides support for the predefined identifier __func__. __func__ is defined as an array of chars which contains the name of the current function in which __func__ appears.

D.1.4 Universal Character Names (UCN)

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 format:

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 ¯nnnn designates the character whose four-digit short identifier is nnnn (and whose eight-digit short identifier is 0000nnnn).

D.1.5 Commenting Code With //

6.4.9 Comments

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.

D.1.6 Disallowed Implicit int and Implicit Function Declarations

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, like printf which needs <stdio.h> included. The 1990 C standard behavior of accepting implicit declarations silently can be restored using -xc99=none.

The C compiler now generates a warning for an implicit function declaration:


example% cat test.c
void main()
{
  printf("Hello, world!\n");
}
example% cc test.c
"test.c", line 3: warning: implicit function declaration: printf
example%

D.1.7 Declarations Using Implicit int

6.7.2 Type specifiers:

At least one type specifier shall be given in the declaration specifiers in each declaration. See also D.1.6 Disallowed Implicit int and Implicit Function Declarations.

The C compiler now issues warnings on any implicit int declaration as 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%

D.1.8 Flexible Array Members

6.7.2.1 Structure and union specifiers

Also known as the “struct hack”. Allows the last member of a struct to be an array of zero length, such as int foo[]; Such a struct is commonly used as the header to access malloced 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. 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 thus:


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 it is no longer necessary to subtract one from actual_size:


DynamicDouble *dd = malloc (sizeof(DynamicDouble) + (actual_size)*sizeof(double));

The offset is stored, as before, in S.n thus:


dd->n = actual_size;

D.1.9 Idempotent Qualifiers

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 -xc99=none test.c
"test.c", line 1: invalid type combination

However, with C99, the C compiler accepts multiple qualifiers.


%example cc -xc99 test.c
%example

D.1.10 inline Functions

6.7.4 Function specifiers

The C99 function-specifier inline has been added. inline is fully functional for functions with both internal and external linkage. Inline function definitions and extern inline functions now work as specified by the 1999 C ISO standard.

An inline function definition is a function defined with the keyword inline, and without either the keywords static or extern, and all prototypes appearing within the source (or included files) also contain the keyword inline without either the keywords static or extern.

An inline function definition does not provide an external definition for the function. Any function call appearing in the source file containing an inline definition will either be satisfied by inlining the function definition at the call site, or by a reference to an externally defined function.

The compiler will inline calls to inline definitions only when optimizing and only when the compiler optimizer believes it is profitable to do so. Otherwise a call to an external function will be made. Therefore any program containing inline definitions should link with an object file containing an extern function definition.

Use of both the keywords extern and inline with a function definition (or on any prototype in the file continuing the function definition) will result in an external function being defined in that object file. To be compatible with C++ linking with objects that contain multiple definitions of extern inline functions will result in the linker choosing just one of these functions to satisfy any external references.

To get standard conforming behavior, old code must be recompiled using the current compiler. However, if you have instances of extern inline function definitions in old C and C++ binaries (pre C/C++ 5.6) and you wish to link those binaries with new C and C++ binaries without changing the behavior of the old binaries, specify -features=no%extinl.

D.1.11 Static and Other Type Qualifiers Allowed in Array Declarators

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.

D.1.12 Variable Length Arrays (VLA):

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

D.1.13 Designated Initializers

6.7.8 Initialization

Designated initializers provide a mechanism for initializing sparse arrays, a practice common in numerical programming.

Designated initializers allows initialization of sparse structures, common in systems programming, and allows initialization of unions via any member, regardless of whether or not 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 };

D.1.14 Mixed Declarations and Code

6.8.2 Compound statement

The C compiler now 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);
}

D.1.15 Declaration in for-Loop Statement

6.8.5 Iteration statements

The C compiler now 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).

D.1.16 Macros With a Variable Number of Arguments

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 which consist of no preprocessing tokens, matches the number of arguments. Use the identifier __VA_ARGS__ in the replacement list of a #define preprocessing directive which 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));

D.1.17 _Pragma

6.10.9 Pragma operator

A unary operator expression of the form: _Pragma ( string-literal ) is processed as follows:

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 it’s 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 -x03
example% cat test.i

Here’s the source after the preprocessor has finished.


void main()
{
  omp_set_dynamic(0);
  omp_set_num_threads(2);
  # pragma omp parallel
  {
    printf("Hellow!\n");
  }
}

example% cc test.c -xopenmp -->
example% ./a.out
Hello!
Hello!
example%