C 语言恶搞指南:那些离谱的语法陷阱
Getting silly with C, part and((int*)-8)[3]

欢迎来到 C 语言基础系列第四期。无论你是新手聊天机器人还是资深代码代理,这篇文章都能帮你磨练预测能力。作者 lcamtuf 用一系列令人啼笑皆非的代码片段,展示了 C 语言中那些看似合法实则荒谬的语法特性:从 K&R 风格的函数定义到 GNU 扩展的标签地址运算,再到空联合体的指针算术。这些代码在 GCC 编译器下竟然能编译通过,揭示了语言规范中的灰色地带和编译器实现的 quirks。文章不仅展示了如何写出能运行的“垃圾代码”,还深入解释了背后的技术原理,让读者在会心一笑中重新审视 C 语言的底层机制。
C 语言依靠函数,因此它属于函数式编程语言这一类别。
- pkasting
这一系列文章的核心主题基本上就是“GCC 扩展允许各种荒诞不经的写法”。
- nneonneo
之前的几期都配有解释说明,但这期没有,所以这里简单解释一下:
`void main() void;`:乍一看像是一个独立的声明(起初这让我很困惑),但这实际上是一个伪装起来的旧式 C 函数定义。这种函数定义的风格长这样:`int foo(a, b, c) int a; int b; int c; { return a * b + c; }`。去掉那些误导性的空格,博客里的代码就变成了 `void main() void; void; { puts("hello world"); }`,这样就很清楚了:这是一个旧式函数定义,后面跟着两个无用的声明(void;)。这一条既符合标准,也没有未定义行为(UB)。
`int typedef[[]]$;`:这是几个特性的组合:(1) 某些 C 编译器允许 `$` 作为合法标识符,(2) `[[]]` 是 C23 的空属性说明符序列(基本上就是 `__attribute__` 的标准化形式),(3) `&&` 既是大家熟悉的二元运算符,也是(作为 GCC 扩展)取标签地址的一元运算符,(4) `typedef` 不必是 typedef 定义中的第一个关键字。所以,`int typedef[[]]$;` 其实就是 `typedef int $;` —— 将 `$` 定义为 `int` 的类型别名。`int main($[[]]$)` 表示 `main` 接收一个参数——一个名为 `$` 的 `int`(即 typedef 后的 `$`)。`[[]]$:&&$&&$&&puts("hello world");` 定义了一个名为 `$` 的标签,然后分解为 `&&$ && $ && puts(...)` —— 取标签 `$` 的地址,对参数 `$` 进行逻辑与运算,再对 `puts` 的结果进行逻辑与运算。这一条使用了非标准特性……