查询含printf连续函数参数的C代码对应ISO C99未定义行为条款
咱们先把问题拆透:这段代码的核心坑点就在main函数里的那行printf——它连续两次调用foo,而foo每次都会覆盖同一个静态数组的内容。
先理清代码里的关键细节
foo里的str是静态存储期数组,意思就是这块内存从程序启动到结束一直存在,每次调用foo都会把新的内容写进去覆盖旧的。而在main的printf调用里:
printf("main %s %s\n", foo(12), foo(4));
这里的两个foo()是printf的函数参数,C99标准根本没规定函数参数的求值顺序(既不是固定左到右,也不是右到左),更关键的是:这两个参数的求值操作之间没有序列点。
对应C99标准的具体条款
这种情况正好属于你提到的“在两个序列点之间,一个对象被修改多次”的未定义行为,对应C99(ISO/IEC 9899:1999)标准里的6.5 表达式 第2款:
Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.
具体来说:
- 这里的“对象”就是静态数组
str,两次foo调用都会修改它的存储内容; - 两个
foo()的求值过程之间没有序列点,完全落在“两个序列点之间”的范围内; - 不管编译器选择先求值哪个
foo,最终都会导致str在无序列点的区间内被修改两次,直接触发未定义行为。
额外说两句
可能有人会觉得:“不就是先调用foo(12)存个值,再调用foo(4)覆盖,最后printf打印两次指针指向的内容,不就是两次都显示4吗?”但问题在于,C标准没给参数求值顺序打包票,而且因为没有序列点,编译器可能做出各种超出预期的优化——这就是未定义行为的可怕之处:标准不保证任何特定结果,程序的行为完全不可预测。
内容的提问来源于stack exchange,提问作者nathan

