第2章 语法“陷阱”
要理解一个C程序,仅仅理解组成该程序的符号是不够的。程序员还必须理解这些符号是如何组合成声明、表达式、语句和程序的。虽然这些组合方式的定 义都很完备,几乎无懈可击,但有时这些定义与人们的直觉相悖,或者容易引起混淆。本章将讨论一些用法和意义与我们想当然的认识不一致的语法结构。
2.1 理解函数声明
当计算机启动时,硬件将调用首地址为0位置的子例程。为了模拟开机启动时的情形,我们必须设计出一个C语句,以显式调用该子 例程。经过一段时间的思考,我们最后得到的语句如下:
(*(void(*) ())0) ();
像这样的表达式恐怕会令每个C程序员的内心都“不寒而栗”。然而,他们大可不必对此望而生畏,因为构造这类表达式其实只有一条简单的规则:按照使用的方式来声明。
任何C变量的声明都由两部分组成:类型以及一组类似表达式的声明符 (declarator)。声明符从表面上看与表达式有些类似,对它求值应该返回一个声明中给定类型的结果。最简单的声明符就是单个变量,如:
float f,g;
这个声明的含义是:当对其求值时,表达式f和g的类型为浮点数类型(float)。因为声明符与表达式的相似,所以我们也可以在声明符中任意使用括号:
float ((f));
这个声明的含义是:当对其求值时,((f))的类型为浮点类型,由此可以推知, f也是浮点类型。
同样的逻辑也适用于函数和指针类型的声明,例如:
float ff();
这个声明的含义是:表达式ff()求值结果是一个浮点数,也就是说,ff是一个返回值为浮点类型的函数。类似地,
float *pf;
这个声明的含义是*pf是一个浮点数,也就是说,pf是一个指向浮点数的指针。
以上这些形式在声明中还可以组合起来,就像在表达式中进行组合一样。因 此,
float *g(),(*h)();
表示*g()与(h)()是浮点表达式。因为()结合优先级高于, g()也就是(g());g是一个函数,该函数的返回值类型为指向浮点数的指针。同理,可以得出h是一个函数指针,h所指向函数的返回值为浮点类型。
一旦我们知道了如何声明一个给定类型的变量,那么该类型的类型转换符就很容易得到了:只需要把声明中的变量名和声明末尾的分号去掉,再将剩余的部分用一个括号整个“封装”起来即可。例如,因为下面的声明:
float (*h)();
表示h是一个指向返回值为浮点类型的函数的指针,因此,
(float (*)())
表示一个“指向返回值为浮点类型的函数的指针”的类型转换符。
拥有了这些预备知识,我们现在可以分两步来分析表达式
(*(void(*)())0)