GCC extensions to the C language


The GNU Compiler Collection (GCC) provides several language features not found in ISO standard C.

GNU GCC logo

As I write this article, GCC has more than 60 different extensions that change the behavior or add functionality to the C language (and also C++). Some of these extensions are very interesting, others are a little bit confusing and a few somewhat dangerous!

It is important to know these GCC extensions because many of them are used by several free and open source projects, including the Linux kernel.

Let’s take a look at some of them?

Nested functions

GCC allows the declaration of nested functions (a function defined inside another function).

The nested function’s name will be local to the block where it is defined and can access all the variables of the containing function that are visible at the point of its definition.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include <stdio.h>

int main(int argc, const char *argv[])
{
        void p (void) { printf("Inside a nested function!\n"); }

        p();

        return 0;
}
$ gcc nested.c -Wall -Werror -o nested
$ ./nested
Inside nested function!

The typeof keyword

We can use the typeof keyword to refer to the type of an expression, making it possible to do generic programming in C!

For example, the max(a,b) macro below operates on any arithmetic type:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>

#define max(a,b) \
        ({ typeof (a) _a = (a); \
           typeof (b) _b = (b); \
           _a > _b ? _a : _b; })

int main(int argc, const char *argv[])
{
        int x = 1024, y = 4096;
        char a = 10, b = 20;
        float j = 1.0, k = 2.0;

        printf("char  max is %d\n", max(a,b));
        printf("int   max is %d\n", max(x,y));
        printf("float max is %f\n", max(j,k));

        return 0;
}
$ gcc typeof.c -Wall -Werror -o typeof
$ ./typeof
char  max is 20
int   max is 4096
float max is 2.000000

Empty structures

GCC allows a C structure to have no members (the structure will have size zero).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#include <stdio.h>

struct empty {
};

int main(int argc, const char *argv[])
{
        printf("sizeof struct empty is %ld\n", sizeof(struct empty));

        return 0;
}
$ gcc empty.c -Wall -Werror -o empty
$ ./empty
sizeof struct empty is 0

In C++, empty structures are part of the language.

Case ranges

In GCC, we can specify a range of consecutive values in a single case label in the format “case start … end”. Crazy, right?

This has the same effect as the proper number of individual case labels, one for each integer value from start to end, inclusive.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>

void ranges(char c)
{
        switch(c) {
        case '0' ... '9':
                printf("[%c] is a number.\n", c);
                break;
        case 'a' ... 'z':
                printf("[%c] is a lowercase letter.\n", c);
                break;
        case 'A' ... 'Z':
                printf("[%c] is an uppercase letter.\n", c);
                break;
        default:
                printf("[%c] is not a valid character!\n", c);
                break;
        }
}

int main(int argc, const char *argv[])
{
        ranges('a');

        return 0;
}
$ gcc ranges.c -Wall -Werror -o ranges
$ ./ranges 
[a] is a lowercase letter.

Zero-length arrays

GCC allows the declaration of zero-length arrays.

A zero-length array can be useful as the last element of a structure that is, for example, a header for a variable-length object. In this case, the name of the zero-length array can be used as a pointer to the dynamically allocated object.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h>

struct msg_header {
        int type;
        int size;
        char content[0];
};

int main(int argc, const char *argv[])
{
        struct msg_header *msg;
        int size = 1024;

        msg = (struct msg_header *) malloc(sizeof (struct msg_header) + size);

        msg->type = 0x01;
        msg->size = size;

        printf("msg header  is at %p\n", (void *)msg);
        printf("msg content is at %p\n", (void *)msg->content);

        return 0;
}
$ gcc array.c -Wall -Werror -o array
$ ./array
msg header  is at 0x55c428265260
msg content is at 0x55c428265268

There are many more extensions but, should we really use them?

Should we use GCC extensions?

All of the examples were compiled with GCC 7.4.0.

The extensions are available without any extra compiler flag because by default GCC 7.4 compiles with -std=gnu11, which means support for the ISO C11 standard plus GCC extensions.

Although some of these extensions are quite useful, the tradeoff is portability. Using the GCC extensions, we could have problems building the program with another compiler.

For example, Clang has its own set of extensions and does not support all of GCC extensions, like nested functions.

$ clang nested.c -o nested
nested.c:5:19: error: function definition is not allowed here
    void p (void) { printf("Inside a nested function!\n"); }
                  ^
nested.c:7:5: warning: implicit declaration of function 'p' is invalid in C99 [-Wimplicit-function-declaration]
    p();
    ^
1 warning and 1 error generated.

If you want to find out if you are using any GCC extension, enable the -pedantic compiler option to generate warnings or the -pedantic-errors compiler option to generate errors.

$ gcc arrayzero.c -o arrayzero -pedantic-errors
arrayzero.c:7:7: error: ISO C forbids zero-size array ‘content’ [-Wpedantic]
  char content[0];
       ^~~~~~~

And if you want to use GCC extensions and still keep the program portable, you can test for the availability of these features doing conditional compilation with the macro __GNUC__, which is always defined under GCC.

The complete list of GCC extensions to the C language is available in the GCC documentation.

What about a challenge now? Can you find two GCC extensions used in the program below, just by reading the source code?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <stdio.h>

int main(int argc, const char *argv[])
{
        int a = 10;
        int x = 0;

        a = x ? : 0b0010;

        printf("a is %d.\n", a);

        return 0;
}

So if you are using GCC in any software you are maintaining, why don’t you try to compile with -pedantic or -pedantic-errors to find out if you are using (by design or mistake) any GCC extension? You may be surprised with the result! ;-)

About the author: Sergio Prado has been working with embedded systems for more than 25 years. If you want to know more about his work, please visit the About Me page or Embedded Labworks website.

Please email your comments or questions to hello at sergioprado.blog, or sign up the newsletter to receive updates.


See also