GNU C Extensions
The GNU Compiler Collection extends the ANSI C standard in a variety of ways. If you don't mind writing blatantly non-standard code, some of these extensions can be convenient and very useful.
About Portability
The problem with non-standard code, however, is that it is not terribly portablecode that takes advantage of GNU extensions doubtless will not compile with a non-GNU compiler. One of the reasons the C language has been so resilient and persistent, besides its fundamental power and flexibility, is that it is standardized and has been ported to every major computing architecture in existence, and probably to most of the minor platforms, too. This standardization makes it extremely portable.
Note - GCC also sports a number of extensions for C++.
So, dauntless reader, you have to choose between the convenience of the extensions or writing ANSI/ISO standard C. My recommendation is to write standard C. When you diverge from the standard, as will happen when using POSIX functions (such as reading and writing file descriptors, covered in Chapter 11, "Input and Output," and Chapter 12, "Working with Files and Directories"), isolate the code in question in a single module and take pains to make sure that your program will compile on a strict ANSI/ISO system. The usual way to accomplish this is to bracket the non-standard code in #ifdef directives. The following code snippet illustrates this approach. It will compile in a strictly ANSI C environment and in a looser, GNU-friendly environment:
#ifdef __STRICT_ANSI__ /* use ANSI/ISO C only here */ #else /* use GNU extensions here */ #endif
The macro __STRICT_ANSI__, if defined either by the user or an ANSI-compatible compiler, indicates that an ANSI-compatible environment is being enforced and code in the first part of the #ifdef block will be compiled. Otherwise, code following the #else directive will be compiled.
For all of the gory details, I will direct the curious reader to GCC's info pages. The extensions covered in this section are ones frequently seen in Linux's system headers, source code, and in many Linux applications.
Note - The ANSI/ISO C standard does not define the behavior of a compiler and the programs it builds when source code uses certain constructs, such as main returning void. At least in theory, anything can happen. An ancient comp.lang.c convention holds that demons will fly out of your nose if you invoke undefined behavior. The point of the quote is that a program is not correct or portable just because it compiles with your compiler on your system.
GNU Extensions
To provide 64-bit storage units, for example, GCC offers the long long type:
long long long_int_var;
On the x86 platform, this definition results in a 64-bit memory location named long_int_var.
Note - The long long type exists in the new draft of the ISO C standard.
Inline Functions
Another GCC-ism you will encounter in Linux header files is the use of inline functions. Provided it is short enough, an inline function expands in your code much as a macro does, thus eliminating the cost of a function call. Inline functions are better than macros, however, because the compiler type-checks them at compile time.
To use the inline functions, insert the keyword inline in front of the function's return type, as shown in the excerpt that follows, then compile with at least -O optimization.
inline void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; }
This short function implements the well-known swap function as an inline function. Note, however, that the inline keyword is only a suggestion to the compiler, not a directive. The compiler decides, using an internal heuristic, whether or not a given function can or will be inlined.
Function and Variable Attributes
The attribute keyword enables you to tell GCC more about your code and helps the code optimizer do a better job. Consider, for example, the standard library functions exit and abort, which never return to their caller. The compiler can generate slightly more efficient code if it knows that they do not return. Of course, user programs may also define functions that do not return. GCC allows you to specify the noreturn attribute for such functions, which acts as a hint to the compiler when it optimizes the function.
Suppose, for example, you have a function named die_on_error that never returns. To use a function attribute, append __attribute__ ((attribute_name)) after the closing parenthesis of the function declaration. Thus, the declaration of die_on_error would look like
void die_on_error(void) __attribute__ ((noreturn));
The function would then be defined normally:
#include <stdlib.h> void die_on_error(void) { /* your code here */ exit(EXIT_FAILURE); }
You can also apply attributes to variables. The aligned attribute, for example, instructs the compiler to align the variable's memory location on a specified byte boundary. The statement
int int_var __attribute__ ((aligned 16)) = 0;
will cause GCC to align int_var on a 16-byte boundary. The packed attribute tells GCC to use the minimum amount of space required for variables or structs. Used with structs, packed causes GCC to remove any padding that it would ordinarily insert for alignment purposes.
If you want to turn off warnings about unused variables, apply the unused attribute to the variable, which informs the compiler that the variable is intended to be unused. This will silence the warning that would otherwise be issued:
float big_salary __attribute__ ((unused));
Comment Delimiters
GCC permits the use of the C++ comment delimiter, //, in C programs unless the -ansi or -traditional compiler options are used. Many other compilers also permit this and it may become part of the new C standard currently being developed. This feature is a real convenience for a new generation of programmers who emerge from university having used C++ much more extensively than C and who are thus more accustomed to typing // rather than /* and */. Consider this extension a convenience.
Using Case Ranges
A terrifically useful extension is case ranges. The syntax looks like this:
case LOWVAL ... HIVAL:
Note that the spaces preceding and following the ellipsis are required. Case ranges are used in switch statements to specify values that fall between LOWVAL and HIVAL inclusive. An example follows:
switch(int_var) { case 0 ... 2: /* your code here */ break; case 3 ... 5: /* more code here */ break; default: /* default code here */ }
The fragment above is equivalent to
switch(int_var) { case 0: case 1: case 2: /* your code here */ break; case 3: case 4: case 5: /* more code here */ break; default: /* default code here */
Case ranges are just a shorthand notation, syntactic sugar, for the traditional switch statement syntax. As you can see, it makes the first code fragment shorter and slightly improves its readability (although veteran C programmers find the idiomatic approach used in the second fragment easier to read).
Constructing Function Names
One GNU extension that can dramatically simplify debugging is to use function names as strings. GCC predefines the variable __FUNCTION__ to be the name of the current function (where flow of control is currently located) as it is written in the source code. Listing 3.8 illustrates how this feature works.
Listing 3.8 Using the __FUNCTION__ Variable
/* * showit.c - Illustrate using the __FUNCTION__ variable */ #include <stdio.h> void foo(void); int main(void) { printf("The current function is %s\n", __FUNCTION__); foo(); return 0; } void foo(void) { printf("The current function is %s\n", __FUNCTION__);}
The output of this program follows:
$ ./showit The current function is main The current function is foo
As you can see, showit obligingly produced the name of the function that was currently executing. This can be quite useful during a debugging session if you are having difficulty locating where a program is running into trouble. Simply insert a few printf statements that use __FUNCTION__ and you will quickly narrow it down.