能否定义类函数式宏保证序列性?C标准合规实现探究
安全实现无未定义行为的类函数式宏get_next()
原代码与预期行为
原C代码实现如下:
const int array[100]; int i = 0; int x; int get_next(void) { return array[i++]; } int foo(void) { return get_next() + get_next(); }
当调用foo()且i初始为0时,行为完全明确:调用结束后i的值为2,返回值为array[0] + array[1]。两次get_next()调用之间存在序列点,不存在未定义行为(UB)。
需求说明
需要将get_next()改写为类函数式宏,需满足以下要求:
- 不依赖实际函数调用
- 不使用编译器扩展
- 保证
get_next() + get_next()的行为无UB,严格符合ISO C标准
可行实现方案
利用逗号运算符的序列点特性,实现如下宏:
#define get_next() (array[i], i++, array[i-1])
若需处理数组越界的情况,可补充边界判断:
#define get_next() (i < 100 ? (array[i], i++, array[i-1]) : 0)
合规性证明(依据ISO C标准)
ISO C标准(C99及后续版本)中关于序列点和表达式求值的核心规则如下:
- 逗号运算符的序列点:逗号运算符(
,)的左操作数求值完成后,会产生一个序列点,右操作数的求值及副作用会严格在该序列点之后执行。 - 表达式求值的独立性:对于二元运算符(如
+)的两个操作数,求值顺序虽未指定,但每个操作数的求值都是一个独立单元,其内部的所有副作用都会在该操作数求值完成前生效。
当get_next() + get_next()展开后,实际表达式为:
(array[i], i++, array[i-1]) + (array[i], i++, array[i-1])
对该表达式的求值过程分析:
- 无论加法的左、右操作数哪个先求值,每个操作数内部的逗号表达式都会严格遵循序列点规则:先读取
array[i](仅作占位,无副作用),经过序列点后执行i++(完成i自增的副作用),再经过序列点后读取array[i-1](此时i的自增已完成,取值确定)。 - 每个
get_next()宏展开对应的逗号表达式,其内部对i的修改与后续读取都被序列点分隔,不存在“无序列的修改与读取”情况,完全符合标准对表达式行为的定义,因此不会产生未定义行为。 - 最终无论操作数求值顺序如何,加法结果均为
array[0] + array[1],i最终值为2,与原函数的预期行为完全一致。
内容的提问来源于stack exchange,提问作者Nate Eldredge
相关产品推荐
相关产品推荐

