GCC 对 C 语言的扩展

大家好!我是大聪明-PLUS

GNU 编译器集合 (GCC) 提供了 ISO 标准 C 中没有的几种语言功能。

在我撰写本文时,GCC 拥有超过60个不同的拓展,它们可以改变 C 语言(以及 C++)的行为或添加功能。其中一些扩展非常有趣,另一些则有点令人困惑,还有一些则有些危险!

了解这些 GCC 扩展非常重要,因为其中许多扩展被多个免费和开源项目使用,包括 Linux 内核。

让我们来看看其中的一些吧?

嵌套函数

GCC 允许声明嵌套函数(在另一个函数内部定义的函数)。

嵌套函数的名称将是其定义块的本地名称,并且可以访问其定义点可见的包含函数的所有变量。

|----------------------------------|--------------------------------------------------------------------------------------------------------------------------------------|
| ` 1 2 3 4 5 6 7 8 9 10 ` | `#include 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!`

typeof 关键字

我们可以使用typedef关键字来引用表达式的类型,从而可以在 C 中进行泛型编程!

例如,下面的*max(a,b)*宏可对任何算术类型进行操作:

|-------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ` | `#include #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 %dn", max(a,b)); printf("int max is %dn", max(x,y)); printf("float max is %fn", 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`

空结构

GCC 允许 C结构没有成员(结构的大小为零)。

|-------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------|
| ` 1 2 3 4 5 6 7 8 9 10 11 ` | `#include struct empty { }; int main(int argc, const char *argv[]) { printf("sizeof struct empty is %ldn", sizeof(struct empty)); return 0; }` |

复制代码
`$ gcc empty.c -Wall -Werror -o empty
$ ./empty
sizeof struct empty is 0`

在 C++ 中,空结构是语言的一部分。

案例范围

在 GCC 中,我们可以在单个case标签中以"case start ... end "的格式指定一系列连续的值。很疯狂吧?

这与适当数量的单独案例标签具有相同的效果,从startend(含)的每个整数值对应一个标签。

|----------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ` 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 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.`

零长度数组

GCC 允许声明零长度数组。

零长度数组可以用作结构体的最后一个元素,例如,用作可变长度对象的标头。在这种情况下,零长度数组的名称可以用作指向动态分配对象的指针。

|----------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ` 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 #include 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 %pn", (void *)msg); printf("msg content is at %pn", (void *)msg->content); return 0; }` |

复制代码
`$ 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 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.`

如果您想知道是否正在使用任何 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 扩展吗?

|-------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------|
| ` 1 2 3 4 5 6 7 8 9 10 11 12 13 ` | `#include int main(int argc, const char *argv[]) { int a = 10; int x = 0; a = x ? : 0b0010; printf("a is %d.n", a); return 0; }` |

相关推荐
Hello.Reader3 小时前
从零到一上手 Protocol Buffers用 C# 打造可演进的通讯录
java·linux·c#
nmxiaocui4 小时前
openssl升级
linux·运维·服务器
初学者_xuan4 小时前
零基础快速了解掌握Linux防火墙-Iptables
linux·服务器·防火墙·linux新手小白
HetFrame4 小时前
John the Ripper jumbo + HashCat 破解压缩密码 ubuntu amd GPU
linux·ubuntu·amd·密码破解·john·压缩密码·hashcat
百锦再5 小时前
在 CentOS 系统上实现定时执行 Python 邮件发送任务
java·linux·开发语言·人工智能·python·centos·pygame
最小的帆也能远航6 小时前
2018年下半年 系统架构设计师 综合知识
linux·运维·服务器
疯子@1236 小时前
nacos1.3.2 ARM 版容器镜像制作
java·linux·docker·容器
Empty_7776 小时前
Linux防火墙-Iptables
linux·运维·服务器
十年编程老舅6 小时前
‌C++左值与右值:从基础概念到核心应用‌
linux·c++·右值引用·c++17·c++左值·c++右值·左值引用