第六章
一、知识点
1、getchar和putchar经常被实现为宏,以避免在每次执行输入或者输出一个字符这样简单的操作时,都要调用相应的函数而造成系统效率的下降。(P100)
2、宏只是对程序的文本起作用,宏提供了一种 对组成C程序的字符进行变换的方式,而 并不作用于程序中的对象。(P101)
3、对于宏定义:(P101)
#define f (x) ((x) - 1)
f(x) 代表的是(x) ((x) - 1)。这是因为 f 和后面的(x)之间多了一个空格,如果希望定义f(x) 为 ((x) - 1),必须这样写:
#define f(x) ((x) - 1)
这一规则不适用于宏调用,而只对宏定义适用。因此,在上面完成宏定义后,f(3) 与 f (3)求值后都等于2。
4、对于求绝对值的一个写法:(P102)
#define abs(x) (((x) > 0)?(x):-(x))
这个式子中的 括号都是有用的,用于预防引起与优先级有关的问题。例如,式子写成以下的情况,就可能带来问题:
#define abs(x) x > 0 ? x : -x
利用它来求abs(a - b),展开后表达式就是
a-b > 0 ? a-b:-a-b
这里的子表达式-a-b显然不是我们想要的-(a-b)。
这个结果表达式也需要用括号括起来,以防止当宏用于一个更大一些的表达式中可能出现的问题。
5、用宏代替函数可能会带来一些错误,比如一个操作数如果在两处被用到,就会被就值两次,比如:(P103)
biggest = max(biggest, x[i++]);
6、宏不是语句,也不是类型定义。(P108)
7、宏有时会被使多个不同变量的类型可在一个地方说明:(P109)
#define FOOTYPE struct foo
FOOTYPE a;
FOOTYPE b, c;
但是这可能会带来错误,还是使用类型定义 type struct foo FOOTYPE; 比较靠谱。比如:
#define T1 struct foo *
typedef struct foo * T2;
从上面两个定义来看,T1和T2从概念上完全符同,但是我们试图用它们来声明多个变量时,就会出现问题:
T1 a, b;
T2 a, b;
第一个声明被扩展为:
struct foo * a, b;
这个语句汇总,a被定义为一个指向结构的指针,而b却被定义为一个结构。
第二个声明则不同,它定义了a和b都是指向结构的指针。
二、问题
1、请使用宏实现max的一个版本,其中max的参数都是整数,要求在宏max的定义中这些整型参数只被求值一次。
答:要解决这个问题只能把每个参数存储在一个临时变量中。如下:
static int max_temp1, max_temp2;
#define max(p, q) (max_temp1 = (p), max_temp2 = (q), max_temp1 > max_temp2 ? max_temp1 : max_temp2)
只要对max宏不是嵌套调用,上面的定义都能正常工作;在max宏嵌套调用的情况下,它不能正常工作。
2、表达式 (x) ((x) - 1) 能否成为一个合法的C表达式?
答:(1)、 如果x是类型名,比如
typedef int x;
这种情况下,(x) ((x) - 1) 等价于 (int) ((int) - 1),这个式子的含义是把常数-1转换为int类型两次。
(2)、x可能为函数指针。如果某个上下文中本应需要函数而实际上却用了函数指针,那么该指针所指向的函数将会自动地被取得并替换这个函数指针。因此,本题中的表达式可以被解释为调用x 所指向的函数,这个函数的参数是 (x) - 1。为了保证(x) - 1是一个合法的表达式,x必须实际地指向一个函数指针数组中的某个元素。假设x的类型为T:
T x;
显而易见,x必须是一个指针,所指向的函数的参数类型是T。可以如下:
typedef void (*T)(void *);