语句arr[i++] += x;是否存在未定义行为?对比编译器告警差异
语句
arr[i++] += x;是否存在未定义行为? 结论
arr[i++] += x;不存在未定义行为,而arr[i++] = arr[i++] + x;确实属于未定义行为,二者的差异核心在于C标准对表达式求值顺序和序列点的规定。
具体分析
复合赋值+=的合法性
C标准对复合赋值运算符(如+=)有明确规则:
- 左操作数
arr[i++]只会被求值一次:这里的i++后缀自增,求值结果是i的当前值(用来定位数组元素),而i自增的副作用会在该表达式的序列点前完成。 - 尽管左、右操作数的求值顺序未指定,但由于左操作数仅求值一次,i只会被修改一次,不存在对同一变量的多次无序列修改,完全符合标准要求。
普通赋值语句的问题
而arr[i++] = arr[i++] + x;的问题在于:
- 表达式中包含两次独立的
i++操作,分别位于赋值运算符的左右两侧。 - C标准规定,赋值运算符的左右操作数求值顺序是未指定的,且两次
i++的副作用(修改i的值)之间没有序列点。这意味着编译器可以任意选择先执行左边或右边的i++,甚至交错执行,导致i的最终值和数组访问的索引完全不可预测,这正是标准定义的未定义行为。
编译器表现的合理性
代码与假设条件
arr[i++] += x; // [1] // arr 是整数类型数组,x 是整数类型变量 // 假设条件: // 1. i 的值是数组 arr 的有效索引,且 i 的前缀/后缀自增/自减结果也是 arr 的有效索引。 // 2. 数组元素和变量 x 的值不会导致加法运算溢出。
编译器警告对比
gcc和clang编译器对语句[1]未发出任何警告,但将[1]替换为以下语句时:
arr[i++] = arr[i++] + x; // [2]
两款编译器均会发出明显警告:gcc编译器警告:
警告:对‘i’的操作可能未定义 [-Wsequence-point] arr[i++] = arr[i++] + x;
clang编译器警告:
警告:对'i'存在多次无序列修改 [-Wunsequenced] arr[i++] = arr[i++] + x;
- 对于合法的
arr[i++] += x;,编译器不会发出警告,因为能识别这是符合标准的表达式。 - 对于存在未定义行为的
arr[i++] = arr[i++] + x;,编译器会明确警告,指出对i的多次无序列修改问题,帮助开发者规避风险。
内容的提问来源于stack exchange,提问作者H.S.
相关产品推荐
相关产品推荐

