C语言
一、GCC扩展语法
1.语句表达式
#include<stdio.h>
void main(void)
{
int a=({1;2;4;})+10;
printf("%d\n",a);//a=14
}这段代码中的 {1;2;4;} 是 GCC 编译器提供的一个特殊扩展语法,叫做 语句表达式(Statement Expression)。
里面可以写多条正常的 C 语言语句。
最关键的一点:整个语句表达式的值,等于最后一条表达式语句的值。
它最大的作用是把复杂的逻辑封装成一个表达式,常用于宏定义(Macro)中,避免宏参数被多次求值的问题。
例如下面的示例:
// 普通宏:如果传入 func(),func 会被执行两次!
#define MAX(a,b) ((a)>(b)?(a):(b))
// 使用语句表达式的宏:安全且能包含局部变量
#define MAX(a,b) \
({ \
typeof(a) _a = (a); \
typeof(b) _b = (b); \
_a > _b ? _a : _b; \
})2.扩展关键字 typeof(a)
这个关键字是C 语言里的“类型推导器”:
typeof(x) y = 20; // 编译器看到后,等同于:int y = 20;因此
typeof(a) _a = (a); //它的作用就是创造一个和A一样类型的变量3.局部标签(Local Labels)
这个功能是 GCC (GNU Compiler Collection) 对 C 语言家族 (主要是 C 和 C++) 进行的非标准扩展,允许您在函数内部的 局部作用域 中声明一个标签,然后使用 goto 语句跳转到这个标签。
GCC 的 局部声明标签 功能通过使用 label 关键字,将标签的声明限制在一个更小的 局部作用域 (例如一个复合语句或代码块) 内,从而避免了标签名称与函数内其他地方的标签或变量名冲突。
void func(int condition) {
// 使用 __label__ 关键字声明局部标签
__label__ local_target;
if (condition > 0) {
goto local_target; // 可以跳转到局部标签
}
// ... 其他代码 ...
// 局部标签的定义
local_target:
printf("Jumped to local target.\n");
// 注意:如果在另一个块内声明了同名的局部标签,
// 它们将是不同的实体。
}二、C语言中应该注意的地方
三、一些思路或方法、杂项
1.空指针偏移量计算
在 C 语言里,内存地址 0 是个很危险的地方。我们不是真的要去“碰”它,我们只是拿它当个“假想的起点”。所以 (struct st*)0 的意思就是:“假装地址 0 的位置,放了一个 struct st 结构体。
现在我想要测量这个结构体的b元素的位置,&((struct st*)0)->b,既然起点是 0,那你顺着往下找,找到 b 的位置,告诉我它距离起点 0 有多远(偏移量是多少)
看完上面的解释,应该知道下面这两个代码的功能是一样的。
typeof ((struct st*)0)->b) c; // 取b的类型来声明c
int c;既然你能把结构体强转成 0 刻度(也就是 (struct st*)0),那如果现在要量的是个数组(比如 int 数组),你是不是也能用同样的方法,把数组强转成 0 刻度呢?
答:假设你有一个整型数组 int arr[10];,你想把它的首地址当成 0 刻度,你可以这样写:(int*)0
如果你想获取数组中第 3 个元素(即 arr[2])相对于 0 刻度的偏移量,你可以这样写:&(((int*)0)[2])
实际上上述操作就是Linux内核源码中的offsetof宏函数:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)2.volatile 修饰函数的历史用法与 noreturn 的演进
Linux源码现象:在早期 Linux 源码(如
volatile void do_exit(long code);)中出现的用法,是早期 GCC(2.5版本前)的非标准扩展。
实际上:与“防止内存优化”无关,而是告诉编译器该函数永远不会返回(No Return)。编译器知道函数不返回,从而省略在堆栈上保存调用者返回地址的操作,节省空间并提升性能。
在现代C语言中,
GCC 扩展:
void func() __attribute__((noreturn));C11 标准:
_Noreturn void func();
