Skip to content

Latest commit

 

History

History
1130 lines (811 loc) · 25.4 KB

c_gnuext.rst

File metadata and controls

1130 lines (811 loc) · 25.4 KB

GNU C Extensions cheatsheet

with __extension__

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

/* with __extension__ */
#define lambda(ret_type, ...)               \
        __extension__                       \
        ({                                  \
                ret_type __fn__ __VA_ARGS__ \
                __fn__;                     \
        })

int main(int argc, char *argv[])
{
        int a = 5566, b = 9527;
        int c = __extension__ 0b101010;
        int (*max) (int, int) = lambda(int, (int x, int y) {return x > y ? x : y; });

        printf("max(%d, %d) = %d\n", a, b, max(a, b));
        printf("binary const c = %x\n", c);

        return 0;
}
#endif

output:

$ gcc -g -Wall -std=c99 -pedantic test.c
$ ./a.out
max(5566, 9527) = 9527
binary const c = 2a

without __extension__

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

/* with __extension__ */
#define lambda(ret_type, ...)               \
        ({                                  \
                ret_type __fn__ __VA_ARGS__ \
                __fn__;                     \
        })

int main(int argc, char *argv[])
{
        int a = 5566, b = 9527;
        int c = 0b101010;
        int (*max) (int, int) = lambda(int, (int x, int y) {return x > y ? x : y; });

        printf("max(%d, %d) = %d\n", a, b, max(a, b));
        printf("binary const c = %x\n", c);

        return 0;
}
#endif

output:

$ gcc -g -Wall -pedantic test.c
test.c: In function 'main':
test.c:17:17: warning: binary constants are a GCC extension [enabled by default]
         int c = 0b101010;
                 ^
test.c:18:40: warning: ISO C forbids nested functions [-Wpedantic]
         int (*max) (int, int) = lambda(int, (int x, int y) {return x > y ? x : y; });
                                        ^
test.c:10:17: note: in definition of macro 'lambda'
                 ret_type __fn__ __VA_ARGS__ \
                 ^
test.c:9:9: warning: ISO C forbids braced-groups within expressions [-Wpedantic]
         ({                                  \
         ^
test.c:18:33: note: in expansion of macro 'lambda'
         int (*max) (int, int) = lambda(int, (int x, int y) {return x > y ? x : y; });
                                 ^
$ ./a.out
max(5566, 9527) = 9527
binary const c = 2a

Binary Constants

ref: Binary Constants

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

int main(int argc, char *argv[])
{
        int a = 0b0101;
        int b = 0x003a;

        printf("%x, %x\n", a, b);

        return 0;
}
#endif

output:

$ gcc -g -Wall -pedantic test.c
test.c: In function 'main':
test.c:9:17: warning: binary constants are a GCC extension [enabled by default]
         int a = 0b0101;
                 ^
$ ./a.out
./a.out
5, 3a

Statements and Declarations in Expressions

ref: Statements and Declarations in Expressions

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

#define square(x)   \
  ({                \
        int y = 0;  \
        y = x * x;  \
        y;          \
   })

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

int main(int argc, char *argv[])
{
        int x = 3;
        int a = 55, b = 66;
        printf("square val: %d\n", square(x));
        printf("max(%d, %d) = %d\n", a, b, max(a, b));
        return 0;
}

#endif

output:

$ ./a.out
square val: 9
max(55, 66) = 66

Locally Declared Labels

ref: Locally Declared Labels

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

#define ARRAYSIZE(arr)                          \
  ({                                            \
        size_t size = 0;                        \
        size = sizeof(arr) / sizeof(arr[0]);    \
        size;                                   \
   })

#define SEARCH(arr, size, target)           \
  ({                                        \
        __label__ found;                    \
        int i = 0;                          \
        int value = -1;                     \
        for (i = 0; i < size; i++) {        \
                if (arr[i] == target) {     \
                        value = i;          \
                        goto found;         \
                }                           \
        }                                   \
        value = -1;                         \
        found:                              \
        value;                              \
   })

int main(int argc, char *argv[])
{
        int arr[5] = {1, 2, 3, 9527, 5566};
        int target = 9527;

        printf("arr[%d] = %d\n",
                SEARCH(arr, ARRAYSIZE(arr), target), target);
        return 0;
}

#endif

output:

$ ./a.out
arr[3] = 9527

Nested Functions

ref: Nested Functions

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

int main(int argc, char *argv[])
{
        double a = 3.0;
        double square(double x) { return x * x; }

        printf("square(%.2lf) = %.2lf\n", a, square(a));
        return 0;
}
#endif

output:

$ ./a.out
square(3.00) = 9.00

Note

The nested function can access all the variables of the containing function that are visible at the point of its definition. This is called lexical scoping.

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

int main(int argc, char *argv[])
{
        int i = 0;

        void up(void) { i++; }
        printf("i = %d\n", i);
        up();
        printf("i = %d\n", i);
        up();
        printf("i = %d\n", i);

        return 0;
}
#endif

output:

./a.out
i = 0
i = 1
i = 2

Note

It is possible to call the nested function from outside the scope of its name by storing its address or passing the address to another function.

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

#define ARRAY_SIZE(arr) sizeof(arr) / sizeof(arr[0])
void print_str(char **arr, int i, char *(*access)(char **arr, int idx))
{
        char *ptr = NULL;

        if (arr == NULL) return;

        ptr = access(arr, i);
        if (ptr != NULL) {
                printf("str = %s\n", ptr);
        }
}

int main(int argc, char *argv[])
{
        char *arr[5] = {"Hello", "World", "Foo", "Bar", NULL};
        char *ptr = NULL;
        int i = 0;
        int offset = 1;

        char *access(char **arr, int idx)
        {
                return arr[idx + offset];
        }

        for (i = 0; i < (ARRAY_SIZE(arr) - offset); i++) {
                print_str(arr, i, access);
        }

    return 0;
}
#endif

output:

$ ./a.out
str = World
str = Foo
str = Bar

Note

A nested function can jump to a label inherited from a containing function, provided the label is explicitly declared in the containing function.

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

int main(int argc, char *argv[])
{
        __label__ end;
        int ret = -1, i = 0;

        void up(void)
        {
                i++;
                if (i > 2) goto end;
        }
        printf("i = %d\n", i); /* i = 0 */
        up();
        printf("i = %d\n", i); /* i = 1 */
        up();
        printf("i = %d\n", i); /* i = 2 */
        up();
        printf("i = %d\n", i); /* i = 3 */
        up();
        printf("i = %d\n", i); /* i = 4 */
        up();
        ret = 0;
end:
        return ret;
}
#endif

output:

$ ./a.out
i = 0
i = 1
i = 2

Note

If you need to declare the nested function before its definition, use auto (which is otherwise meaningless for function declarations).

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

int main(int argc, char *argv[])
{
        int i = 0;
        auto void up(void);

        void up(void) { i++; }
        printf("i = %d\n", i); /* i = 0 */
        up();
        printf("i = %d\n", i); /* i = 1 */
        up();
        printf("i = %d\n", i); /* i = 2 */
        up();
        return 0;
}
#endif

output:

$ ./a.out
i = 0
i = 1
i = 2

Referring to a Type with typeof

ref: Referring to a Type with typeof

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

#define pointer(T)  typeof(T *)
#define array(T, N) typeof(T [N])

int g_arr[5];

int main(int argc, char *argv[])
{
        int i = 0;
        char **ptr = NULL;

        /* This declares _val with the type of what ptr points to. */
        typeof (*g_arr) val = 5566;
        /* This declares _arr as an array of such values. */
        typeof (*g_arr) arr[3] = {1, 2,3};
        /* This declares y as an array of pointers to characters.*/
        array (pointer (char), 4) str_arr = {"foo", "bar", NULL};

        printf("val: %d\n", val);
        for (i = 0; i < 3; i++) {
                printf("arr[%d] = %d\n", i, arr[i]);
        }
        for (i = 0, ptr = str_arr; *ptr != NULL ; i++, ptr++) {
                printf("str_arr[%d] = %s\n", i, *ptr);
        }

        return 0;
}
#endif

output:

$ ./a.out
val: 5566
arr[0] = 1
arr[1] = 2
arr[2] = 3
str_arr[0] = foo
str_arr[1] = bar

Conditionals with Omitted Operands

ref: Conditionals with Omitted Operands

Note

The middle operand in a conditional expression may be omitted. Then if the first operand is nonzero, its value is the value of the conditional expression.

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

int main(int argc, char *argv[])
{
        int x = 1, y = 0;
        int z = -1;

        /* equivalent to x ? x : y */
        z = x ? : y;
        printf("z = %d\n", z);
        return 0;
}

output:

$ ./a.out
z = 1

Arrays of Length Zero

ref: Zero-length arrays

Note

Zero-length arrays are allowed in GNU C. They are very useful as the last element of a structure which is really a header for a variable-length object

#include <stdlib.h>
#include <errno.h>
#include <string.h>

#define CHECK_NULL(ptr, fmt, ...)                   \
        do {                                        \
                if (!ptr) {                         \
                        printf(fmt, ##__VA_ARGS__); \
                        goto End;                   \
                }                                   \
        } while(0)

/* array item has zero length */
typedef struct _list {
        int len;
        char *item[0];
} list;

int main(int argc, char *argv[])
{

        int ret = -1, len = 3;
        list *p_list = NULL;

        p_list = (list *)malloc(sizeof(list) + sizeof(char *) * len);
        CHECK_NULL(p_list, "malloc fail. [%s]", strerror(errno));

        p_list->item[0] = "Foo";
        p_list->item[1] = "Bar";
        p_list->item[2] = NULL;

        printf("item[0] = %s\n", p_list->item[0]);
        printf("item[1] = %s\n", p_list->item[1]);
        printf("item[2] = %s\n", p_list->item[2]);

        ret = 0;
End:

        if (p_list)
                free(p_list);

        return ret;
}

#endif

output:

$ ./a.out
item[0] = Foo
item[1] = Bar
item[2] = (null)

Note

GCC allows static initialization of flexible array members

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

typedef struct _list {
        int len;
        int item[];
} list;

#define PRINT_LIST(l)                             \
        do {                                      \
                int i = 0;                        \
                for (i = 0; i < l.len; i++) {     \
                        printf("%d ", l.item[i]); \
                }                                 \
                printf("\n");                     \
        } while(0)

int main(int argc, char *argv[])
{
        static list l1 = {3, {1, 2, 3}};
        static list l2 = {5, {1, 2, 3, 4, 5}};

        PRINT_LIST(l1);
        PRINT_LIST(l2);
        return 0;
}

#endif

output:

$ ./a.out
1 2 3
1 2 3 4 5

Variadic Macros

ref: Variadic Macros

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

#define DEBUG_C99(fmt, ...)     fprintf(stderr, fmt, ##__VA_ARGS__)
#define DEBUG_GNU(fmt, args...) fprintf(stderr, fmt, ##args)

int main(int argc, char *argv[])
{
        DEBUG_C99("ISO C supported variadic macros\n");
        DEBUG_GNU("GNU C supported variadic macors\n");

        DEBUG_C99("ISO C format str = %s\n", "Foo");
        DEBUG_GNU("GNU C format str = %s\n", "Bar");

        return 0;
}
#endif

output:

$ ./a.out
ISO C supported variadic macros
GNU C supported variadic macors
ISO C format str = Foo
GNU C format str = Bar

Compound Literals (cast constructors)

ref: Compound Literals

Note

A compound literal looks like a cast containing an initializer. Its value is an object of the type specified in the cast, containing the elements specified in the initializer

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

int main(int argc, char *argv[])
{
        struct foo {int a; char b[3]; } structure = {};

        /* compound literals (cast constructors )*/

        structure = ((struct foo) { 5566, 'a', 'b'});
        printf("a = %d, b = %s\n", structure.a, structure.b);

        /* equal to */

        struct foo temp = {5566, 'a', 'b'};
        structure = temp;

        printf("a = %d, b = %s\n", structure.a, structure.b);

        return 0;
}
#endif

output:

$ ./a.out
a = 5566, b = ab
a = 5566, b = ab

Note

If the object being initialized has array type of unknown size, the size is determined by compound literal size

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

int main(int argc, char *argv[])
{
        /* The size is determined by compound literal size */

        static int x[] = (int []) {1, 2, 3, 4, 5};
        static int y[] = (int [3]) {1};
        int i = 0;

        for (i = 0; i < 5; i++) printf("%d ", x[i]);
        printf("\n");

        for (i = 0; i < 3; i++) printf("%d ", y[i]);
        printf("\n");

        /* equal to */

        static int xx[] = {1, 2, 3, 4, 5};
        static int yy[] = {1, 0, 0};

        for (i = 0; i < 5; i++) printf("%d ", xx[i]);
        printf("\n");

        for (i = 0; i < 3; i++) printf("%d ", yy[i]);
        printf("\n");

        return 0;
}
#endif

output:

./a.out
1 2 3 4 5
1 0 0
1 2 3 4 5
1 0 0

Case Ranges

ref: Case Ranges

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

int foo(int a)
{
        switch (a) {
                case 1 ... 3:
                        return 5566;
                case 4 ... 6:
                        return 9527;
        }
        return 7788;
}

int main(int argc, char *argv[])
{
        int b = 0;

        b = foo(1);
        printf("b = %d\n", b);

        b = foo(5);
        printf("b = %d\n", b);

        b = foo(10);
        printf("b = %d\n", b);

        return 0;
}
#endif

output:

$ ./a.out
b = 5566
b = 9527
b = 7788

Warning

Be careful, write spaces around the ... (ex: r1 ... r2), for otherwise it may be parsed wrong when you use it with integer values

Designated Initializers

ref: Initializers

Array initializer

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

#define ARRLEN 6

int main(int argc, char *argv[])
{
        /* ISO C99 support giving the elements in any order */
        int a[ARRLEN] = {[5] = 5566, [2] = 9527};
        /* equal to (ISO C90)*/
        int b[ARRLEN] = {0, 0, 9527, 0, 0, 5566};
        register int i = 0;

        for (i = 0; i < ARRLEN; i++) printf("%d ", a[i]);
        printf("\n");

        for (i = 0; i < ARRLEN; i++) printf("%d ", a[i]);
        printf("\n");

        return 0;
}
#endif

output:

$ # compile in C90 mode
$ gcc -std=c90 -pedantic test.c
test.c: In function 'main':
test.c:12:26: warning: ISO C90 forbids specifying subobject to initialize [-Wpedantic]
         int a[ARRLEN] = {[5] = 5566, [2] = 9527};
                          ^
test.c:12:38: warning: ISO C90 forbids specifying subobject to initialize [-Wpedantic]
         int a[ARRLEN] = {[5] = 5566, [2] = 9527};
                                      ^

$ # compile in C99 mode
$ gcc -std=c99 -pedantic test.c
$ ./a.out
0 0 9527 0 0 5566
0 0 9527 0 0 5566

Note

GNU C also support to initialize a range of elements to the same value

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

#define ARRLEN 10

int main(int argc, char *argv[])
{
        int arr[ARRLEN] = { [2 ... 5] = 5566, [7 ... 9] = 9527};
        register i = 0;

        for (i = 0; i< ARRLEN; i++) printf("%d ", arr[i]);
        printf("\n");

        return 0;
}
#endif

output:

$ gcc -pedantic test.c
test.c: In function 'main':
test.c:11:32: warning: ISO C forbids specifying range of elements to initialize [-Wpedantic]
         int arr[ARRLEN] = { [2 ... 5] = 5566, [7 ... 9] = 9527};
                                ^
test.c:11:29: warning: ISO C90 forbids specifying subobject to initialize [-Wpedantic]
         int arr[ARRLEN] = { [2 ... 5] = 5566, [7 ... 9] = 9527};
                             ^
test.c:11:50: warning: ISO C forbids specifying range of elements to initialize [-Wpedantic]
         int arr[ARRLEN] = { [2 ... 5] = 5566, [7 ... 9] = 9527};
                                                  ^
test.c:11:47: warning: ISO C90 forbids specifying subobject to initialize [-Wpedantic]
         int arr[ARRLEN] = { [2 ... 5] = 5566, [7 ... 9] = 9527};
                                               ^
$ ./a.out
0 0 5566 5566 5566 5566 0 9527 9527 9527

structure & union initializer

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

typedef struct _point {int x, y; } point;
typedef union _foo {int i; double d; } foo;


int main(int argc, char *argv[])
{
        point a = { 5566, 9527 };
        /* GNU C support initialize with .fieldname = */
        point b = { .x = 5566, .y = 9527 };
        /* obsolete since GCC 2.5 */
        point c = { x: 5566, y: 9527 };
        /* specify which element of the union should be used */
        foo bar = { .d = 5566 };

        printf("a.x = %d, a.y = %d\n", a.x, a.y);
        printf("b.x = %d, b.y = %d\n", b.x, b.y);
        printf("c.x = %d, c.y = %d\n", c.x, c.y);
        printf("bar.d = %.2lf\n", bar.d);

        return 0;
}
#endif

output:

$ gcc -pedantic test.c
test.c: In function 'main':
test.c:15:21: warning: ISO C90 forbids specifying subobject to initialize [-Wpedantic]
         point b = { .x = 5566, .y = 9527 };
                     ^
test.c:15:32: warning: ISO C90 forbids specifying subobject to initialize [-Wpedantic]
         point b = { .x = 5566, .y = 9527 };
                                ^
test.c:17:22: warning: obsolete use of designated initializer with ':' [-Wpedantic]
         point c = { x: 5566, y: 9527 };
                      ^
test.c:17:31: warning: obsolete use of designated initializer with ':' [-Wpedantic]
         point c = { x: 5566, y: 9527 };
                               ^
test.c:19:21: warning: ISO C90 forbids specifying subobject to initialize [-Wpedantic]
         foo bar = { .d = 5566 };
                     ^
test.c:24:9: warning: ISO C90 does not support the '%lf' gnu_printf format [-Wformat=]
         printf("bar.d = %.2lf\n", bar.d);
         ^
$ a.out
a.x = 5566, a.y = 9527
b.x = 5566, b.y = 9527
c.x = 5566, c.y = 9527
bar.d = 5566.00

Unnamed Structure and Union Fields

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

struct foo {
        int a;
        union {
                int b;
                char byte[4];
        };
        int d;
};

int main(int argc, char *argv[])
{

        struct foo bar = { 0x1a, { 0x2b }, 0x3c };
        int i = 0;

        printf("%x, %x, %x\n", bar.a, bar.b, bar.d);

        /* on little machine, we will get 2b 0 0 0 */
        for (i = 0; i < 4; i++) printf("%x ", bar.byte[i]);
        printf("\n");

        return 0;
}
#endif

output:

# without gcc options -std=c11 will raise warning
$ gcc -g -Wall -pedantic test.c
test.c:12:10: warning: ISO C90 doesn't support unnamed structs/unions [-Wpedantic]
         };
          ^
# with gcc options -std=c11 will not raise warning
$ gcc -g -Wall -pedantic -std=c11 test.c
$ ./a.out
1a, 2b, 3c
2b 0 0 0

Note

Unnamed field must be a structure or union definition without a tag like struct { int a; };. If -fms-extensions is used, the field may also be a definition with a tag such as struct foo { int a; };

#ifndef __GNUC__
#error "__GNUC__ not defined"
#else

#include <stdio.h>

struct foo {
        int b;
        int c;
};

struct bar {
        int a;
        struct foo;
        int d;
};

int main(int argc, char *argv[])
{
        struct bar baz = { 0x1a, { 0x2b, 0x00 }, 0x3c };

        printf("%x, %x, %x, %x\n", baz.a, baz.b, baz.c, baz.d);

        return 0;
}
#endif

output:

$ gcc -g -Wall -pedantic -std=c11 -fms-extensions test.c
$ ./a.out
1a, 2b, 0, 3c