GCC 对 C 语言的扩展
大家好!我是大聪明-PLUS!
GNU 编译器集合 (GCC) 提供了 ISO 标准 C 中没有的几种语言功能。
在我撰写本文时,GCC 拥有超过60个不同的拓展,它们可以改变 C 语言(以及 C++)的行为或添加功能。其中一些扩展非常有趣,另一些则有点令人困惑,还有一些则有些危险!
了解这些 GCC 扩展非常重要,因为其中许多扩展被多个免费和开源项目使用,包括 Linux 内核。
让我们来看看其中的一些吧?
嵌套函数
GCC 允许声明嵌套函数(在另一个函数内部定义的函数)。
嵌套函数的名称将是其定义块的本地名称,并且可以访问其定义点可见的包含函数的所有变量。
|
|
$ gcc nested.c -Wall -Werror -o nested
$ ./nested
Inside nested function!
typeof 关键字
我们可以使用typedef关键字来引用表达式的类型,从而可以在 C 中进行泛型编程!
例如,下面的max(a,b)宏可对任何算术类型进行操作:
|
|
$ gcc typeof.c -Wall -Werror -o typeof
$ ./typeof
char max is 20
int max is 4096
float max is 2.000000
空结构
GCC 允许 C结构没有成员(结构的大小为零)。
|
|
$ gcc empty.c -Wall -Werror -o empty
$ ./empty
sizeof struct empty is 0
在 C++ 中,空结构是语言的一部分。
案例范围
在 GCC 中,我们可以在单个case标签中以"case start ... end ”的格式指定一系列连续的值。很疯狂吧?
这与适当数量的单独案例标签具有相同的效果,从start到end(含)的每个整数值对应一个标签。
|
|
$ gcc ranges.c -Wall -Werror -o ranges
$ ./ranges
[a] is a lowercase letter.
零长度数组
GCC 允许声明零长度数组。
零长度数组可以用作结构体的最后一个元素,例如,用作可变长度对象的标头。在这种情况下,零长度数组的名称可以用作指向动态分配对象的指针。
|
|
$ gcc array.c -Wall -Werror -o array
$ ./array
msg header is at 0x55c428265260
msg content is at 0x55c428265268
还有许多其他扩展,但是我们真的应该使用它们吗?
我们应该使用 GCC 扩展吗?
所有示例均使用 GCC 7.4.0 编译。
这些扩展无需任何额外的编译器标志即可使用,因为默认情况下 GCC 7.4 使用-std=gnu11进行编译,这意味着支持 ISO C11 标准和 GCC 扩展。
虽然其中一些扩展确实很有用,但其代价是可移植性。使用 GCC 扩展,我们可能会在使用其他编译器构建程序时遇到问题。
例如,Clang有自己的一组扩展,并且不支持所有 GCC 扩展,如嵌套函数。
$ clang nested.c -o nested
nested.c:5:19: error: function definition is not allowed herevoid 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.
如果您想知道是否正在使用任何 GCC 扩展,请启用-pedantic编译器选项来生成警告或启用-pedantic-errors编译器选项来生成错误。
$ gcc arrayzero.c -o arrayzero -pedantic-errors
arrayzero.c:7:7: error: ISO C forbids zero-size array ‘content’ [-Wpedantic]char content[0];^~~~~~~
并且,如果您想使用 GCC 扩展并仍保持程序的可移植性,则可以使用宏__GNUC__进行条件编译来测试这些功能的可用性,该宏始终在 GCC 下定义。
GCC文档中提供了 C 语言的 GCC 扩展的完整列表。
现在来个挑战吧!你能通过阅读源代码,找到下面程序中使用的两个 GCC 扩展吗?
|
|