P99
|
As extensions to C89, C99 offers some features that improve the possibilities of programming efficiently and portably at the same time. There are four of these new concepts that are particularly important for P99 and without them P99 wouldn't be possible.
The preprocessor now has a feature that previously only C functions had: a macro may accept an argument list that may vary in size. As with functions, such a macro is defined with ‘...’ in the argument list to indicate a list following the initial, named, arguments:
The variable-length list is then referred to using the reserved identifier VA_ARGS
.
This functionality of C99 allows us e.g to implement macros for Default arguments to functions and to perform Code Unrolling.
The new keyword inline
is borrowed from C++ with some slightly changed semantics. The important part for P99 is that functions can be defined in a header file (and not only declared) when specified as inline
.
This allows us to define small wrappers in header files using macros, without generating conflicts in different compilation units. By that we can avoid one of the major drawbacks of C macro programming: a macro cannot define another macro. In addition functions, when compared to macros, have other advantages
In C89, initialization of structures can be tedious and error prone:
Components are initialized in the order of the type declaration. Here the 0
in the initializer is used to initialize the component A.a
and the 1
for A.b.
Whenever the structure toto
changes during the development process, we would have to revisit all initializations to see whether or not they remain consistent:
a
and b
changes, the order of the expressions must be inverteda
or b
, the initialization of b
by 1
in the example is replaced by the default initialization, namely 0.0
.Keeping track of these may be particularly difficult if the components are of similar types, such that an initializer for one is valid for the other.
With designated initializers this situation changes substantially:
By this means we avoid all of the problems mentioned above. This scheme is robust against reordering and insertion of components. In a certain sense it is also robust against the renaming of components: all initializations will then simply fail at compile time, so it is easy to identify problems.
For a more detailed discussion of initialization and P99 see Variable initialization.
A compound literal is syntactically given as a compound initializer and a cast such as
It is best seen as defining a temporary object of the requested type, initialized using the same rules that apply to a named variable of that type.
Example: The following code returns the pointer to a character array that is initialized with all ‘a’
and a terminating 0
character. The array is a valid object until the program leaves the current block.
It would be equivalent to the following
Using the compound literal here has the advantage that no other non-const reference to the temporary is exposed.
The compound literal syntax is not always very easy to read; in fact it might even hurt your eyes. P99 gives you a shortcut for compound literals that are initialized from the all 0
initializer. With that the above could have been written:
Per se, this is not a new feature of C99 but had been present before. The preprocessor has two special rules, one that applies generally to macros and the other that applies only to functional macros:
Theses features can be used to define a macro and another identifier that have the same name. It is sometimes used for a test if some functionality is present on a platform. E.g on my computer I have
This can be used as follows
But we may equally use this technique for a function symbol. POSIX explicitly allows this for example for the functions in stdio.h
Lets have a look at a randomly selected function from stdio and suppose it would be given as follows:
(Yes this evaluates C
twice.) With that, these uses of putc
are still valid:
The example above with putc
has a particular pitfall if we have the above definitions in a header file and then include this file at the place where we define the function:
This will simply explode since the preprocessor will expand the functional reference to putc
. This can be explicitly avoided by undefining the macro before the definition, but for this the implementor of putc
has to know that it is also a macro.
With P99, we use this technique to overload a function to provide it with Default arguments to functions. A macro defined in that way will avoid this pitfall: if it is called with the same number of arguments (or more) that are all non-empty, it will produce the same token sequence as if the macro had not been defined.
The traditional approach in C had been to specify meta information for the compiler in so called pragmas:
The inconvenience of such a construct is that it has always to be on a line of its own and cannot be placed in a macro. For that reason most compilers provided extensions that let the programmer place meta information more precisely at some specific point of the code, e.g gcc has an attribute
extension for that.
C99 adds a keyword to overcome that difficulty and to normalize the link between macros and #pragma:
_Pragma
.
P99 uses this feature for extensions concerning OpenMP, in particular the P99_PARALLEL_FOR and P99_PARALLEL_FORALL macros.