getc宏实现中"对stream多次求值"的具体含义是什么?
关于getc宏对stream参数多次求值的说明
getc可能被实现为宏,该宏会对stream参数进行多次求值。
要理解“多次求值”的含义,首先要区分C语言中宏和函数的参数处理逻辑:
- 函数的参数会先完成求值,再把计算结果传入函数内部,不管参数在函数内部被使用多少次,参数对应的表达式只会执行1次
- 宏是纯文本替换,参数对应的表达式会直接代入宏的定义代码里,宏的定义里用了多少次参数,这个表达式就会被执行多少次,也就是所谓的“多次求值”
举个实际的简化示例,假设getc的宏实现逻辑大致如下:
#define getc(s) ( ( (s)->_flag & _IOREAD ) ? __getc_impl(s) : EOF )
如果你调用时传入的stream不是一个普通的变量,而是带有副作用的表达式,比如:
// 假设get_next_stream()每次调用会返回不同的打开文件流,自带偏移逻辑 int c = getc(get_next_stream());
经过宏替换后代码会变成:
int c = ( ( (get_next_stream())->_flag & _IOREAD ) ? __getc_impl(get_next_stream()) : EOF );
这时候get_next_stream()会被执行两次,第一次用来判断流合法性,第二次用来读取字符,导致你实际读取的是第二次调用返回的流的内容,逻辑完全不符合预期。
常见的有风险的传入参数还包括带自增/自减的指针表达式,比如getc(fp++),如果触发多次求值会导致fp多偏移1位,直接跳过一个有效数据,甚至出现越界访问。
如果getc是函数实现就不存在这个问题,不管函数内部处理时用到stream多少次,传入的get_next_stream()或者fp++都只会求值一次。
内容的提问来源于stack exchange,提问作者Agrudge Amicus
相关产品推荐
相关产品推荐

