C陷进与缺陷

1、词法分析

C语言的符号(token)之间的空白包括空格符制表符换行符
编译器将程序分解成符号(token)的方法是:从左到右一个字符一个字符地读入,如果该字符可能组成一个符号,那么再读入下一个字符,判断已经读入的两个字符组成的字符串是否可能是一个符号的组成部分;如果可能,继续读入下一个字符,重复上述判断,直到读入的字符组成的字符串已不再可能组成一个有意义的符号
可以归纳为一个很简单的规则:每一个符号(token)应该包含尽可能多的字符。这个处理策略被称为贪心法

2、字符与字符串

双引号引起的字符串,代表的是一个指向无名数组起始字符指针,该数组被双引号之间的字符以及一个额外二进制值为0字符'\0'初始化。

3、理解函数声明:

当计算机启动时,硬件将调用首地址为0位置子例程

( *(void(*)()) 0)();

任何C变量声明都由两部分组成:类型以及一组类似表达式声明符(declarator)声明符从表面上看与表达式有些类似,对它求值应该返回声明给定类型的结果

float *g(), (*h)();

表示*g()(*h)()浮点表达式。因为()结合优先级高于**g()也就是*(g())g是一个函数,该函数的返回值类型指向浮点数指针
同理,可以得出h是一个函数指针h指向函数返回值浮点类型
一旦我们知道了如何声明一个给定类型变量,那么该类型的类型转换符就很容易得到了:只需要把声明中的变量名声明末尾分号去掉,再将剩余的部分用一个括号把整个封装起来即可。例如下面的声明:

float (*h)();

表示h是一个指向返回值为浮点类型的函数的指针,因此,

(float (*)())

表示一个指向返回值为浮点类型的函数的指针类型转换符
拥有了这些预备知识,我们现在可以分两步来分析表达式( *(void(*)()) 0)()
第一步,假定变量fp是一个函数指针,调用fp所指向的函数方法如下:

(*fp)();

因为fp是一个函数指针,那么*fp就是该指针所指向的函数,所以(*fp)()就是调用该函数的方式。ANSI C标准允许程序员将上式简写为fp(),但是一定要记得这种写法只是一种简写形式
在表达式(*fp)()中,*fp两侧的括号非常重要,因为函数运算符()的优先级高于单目运算符*。如果*fp两侧没有括号,那么*fp()实际上与*(fp())的含义完全一致,ANSI C把它作为*((*fp)())的简写形式。
现在,剩下的问题就只是找到一个恰当的表达式来替换fp。我们将在分析的第二步来解决这个问题。如果C编译器能够理解我们大脑中对于类型的认识,那么我们可以这样写:

(*0)();

上式并不能生效,因为运算符*必须要一个指针来做操作数。而且,这个指针还应该是一个函数指针,这样经运算符*作用后的结果才能作为函数被调用。因此,在上式中必须对0类型转换,转换后的类型可以大致描述为:指向返回值为void类型的函数的指针
如果fp是一个指向返回值为void类型的函数的指针,那么(*fp)()的值为voidfp的声明如下:

void (*fp)();

因此,我们可以用下式来完成调用存储位置为0子例程

(*fp)();

我们一旦知道如何声明一个变量,也就自然知道如何对一个常数进行类型转换,将其转型为该变量的类型:只需要在变量声明中将变量名去掉即可。因此,将常数0转型为指向返回值为void的函数的指针类型,可以这样写:

(void (*)())0

因此,我们可以用(void (*)())0来替换fp,从而得到:

( *(void (*)()) 0)();

猜你喜欢

转载自blog.csdn.net/Hongwei_1990/article/details/89482355
今日推荐