看起来您在这里受到未指定行为的影响,因为初始化列表表达式的评估顺序未指定,来自草案 C99 标准部分6.7.8
:
未指定初始化列表表达式中任何副作用发生的顺序。133)
注释 133 说:
特别是,评估顺序不必与子对象初始化的顺序相同。
据我所知,支持注释的规范文本133
将来自部分6.5
:
除非稍后指定 [...] 子表达式的求值顺序和副作用发生的顺序都是未指定的。
我们可以看到初始化6.8
器是(强调我的)的完整表达式:
完整表达式是不属于另一个表达式或声明符的表达式。
下面的每一个都是一个完整的表达式:一个初始化器;[...]
在回顾了我的一个旧 C++ 答案后,该答案涵盖了初始化程序中的序列点,并将完整表达式放置在不同的位置,然后我最初得出结论,我两次意识到6.7.8
包含初始化程序中的语法:
initializer:
assignment-expression
{ initializer-list }
{ initializer-list , }
initializer-list:
designationopt initializer
initializer-list , designationopt initializer
我最初没有注意到这一点,并认为完整表达式的语句应用于上述语法中的顶部元素。
我现在相信像 C++ 一样,完整表达式适用于初始化器列表中的每个初始化器,这使我之前的分析不正确。
缺陷报告 439证实了我的怀疑,确实是这种情况,它包含以下示例:
#include <stdio.h>
#define ONE_INIT '0' + i++ % 3
#define INITIALIZERS [2] = ONE_INIT, [1] = ONE_INIT, [0] = ONE_INIT
int main()
{
int i = 0;
char x[4] = { INITIALIZERS }; // case 1
puts(x);
puts((char [4]){ INITIALIZERS }); // case 2
puts((char [4]){ INITIALIZERS } + i % 2); // case 3
}
它说:
在每次使用 INITIALIZERS 宏时,变量 i 都会增加三倍。在情况 1 和 2 中,没有未定义的行为,因为增量是在相对于彼此不确定排序的表达式中,而不是未排序的。
所以里面的每个初始化INITIALIZERS
器都是一个完整的表达式。
由于此缺陷报告针对 C11,因此值得注意的是,在有关此问题的规范文本中,C11 比 C99 更冗长,它说:
初始化列表表达式的计算相对于彼此的顺序是不确定的,因此任何副作用发生的顺序是未指定的。152)
values
如果在将 in 中的各个元素分配给之前评估以下表达式,则会出现未定义的行为:
values[0] + values[5]
或者:
values[5]/10
这是未定义的行为,因为使用不确定的值会调用未定义的行为。
在这种特定情况下,最简单的解决方法是手动执行计算:
int values[10] = {
[0]=197,[2]=-100,[5]=350,
[3]= 197 + 350,
[9]= 350/10
};
还有其他替代方法,例如在初始化之后对元素3
进行分配。9