C语言 多维数组访问是否按顺序进行?

esbemjvw  于 2023-02-21  发布在  其他
关注(0)|答案(3)|浏览(166)

请看以下内容:

int a(void) {
    puts("a");
    return 0;
}

int b(void) {
    puts("b");
    return 1;
}

int c(void) {
    puts("c");
    return 2;
}

int d(void) {
    puts("d");
    return 3;
}

以下内容是否具有可预测的行为?

int arr[4][4][4][4];
arr[a()][b()][c()][d()] = 1;

是否保证按以下顺序打印:

a
b
c
d

我知道以下构造是无效的:

int i;
i = i++;

这是因为=是一个无序的运算符,所以先计算i还是先计算i++是未定义的。在另一个序列点之前访问和修改单个对象是未定义的行为。
换句话说,以下内容是否有效:

int i = 0, arr[4][4][4][4];
arr[i++][i++][i++][i++] = 1;

或者,它是否会由于对i的无序修改和访问而调用未定义的行为?
根据C标准,在索引多维数组时,每个连续的[]之间是否有一个定义的序列点?
需要说明的是,这两个例子都不涉及优先级、隐式括号的位置、运算符对操作数进行运算的顺序等问题,问题在于排序,即 * operand self * 的求值顺序。

ki1q1bka

ki1q1bka1#

以下内容是否具有可预测的行为?

int arr[4][4][4][4];
arr[a()][b()][c()][d()] = 1;
    • 没有**

虽然数组元素的计算将从左到右进行,因为一个是下一个的操作数,但不能保证数组索引本身将从左到右进行计算。
具体而言,在arr[a()][b()]之前评价arr[a()],在arr[a()][b()][c()]之前评价arr[a()][b()],在arr[a()][b()][c()][d()]之前评价arr[a()][b()][c()],但是也可以按照任意的顺序评价a()b()c()d()
关于表达式的C standard第6.5p3节规定:
运算符和操作数的分组由语法指示。除非后面指定,否则子表达式的副作用和值计算是无序的
www.example.com关于数组下标的章节没有提到操作数E1[E2]的排序,尽管它确实声明了先前的表达式完全等价于(*((E1)+(E2)))。然后,查看www.example.com关于间接运算符*的章节和6.5.6关于加法运算符+的章节,6.5.2.1 regarding array subscripting makes no mention of sequencing of the operands E1[E2] , although it does state that the prior expression is exactly equivalent to (*((E1)+(E2))) . Then, looking at section 6.5.3.2 regarding the indirection operator * and section 6.5.6 regarding the additive operator + , neither make any mention of the evaluation of their operands being sequenced in any way. So 6.5p3 applies, and the functions a , b , c , and d may be called in any order.
出于同样的原因,这:

arr[i++][i++][i++][i++] = 1;

触发undefined behavior,因为数组索引的计算彼此之间没有顺序关系,并且在没有序列点的情况下对同一对象有多个副作用。

6vl6ewon

6vl6ewon2#

多维数组访问的索引不保证以任何特定的顺序求值。A demonstration(yano提供)显示了从左到右调用的函数,但在Godbolt中选择不同的编译器从右到左求值。
经过进一步调查,第二个代码示例使用Clang引起警告:

warning: multiple unsequenced modifications
      to 'i' [-Wunsequenced]
        arr[i++][i++][i++][i++] = 1;
             ^    ~~

多维数组访问可以被分解为一系列(array)[index],其中array是多维数组的较高维度(arr本身是最高维度),并且index是索引表达式,诸如函数调用或i++表达式。
该标准认为lhs[rhs]等价于*((lhs)+(rhs)),因此不确定是任何给定的index,还是它所索引的array首先被求值,因为+操作符是无序的。在array不是arr本身的所有情况下,对array的求值包括在甚至更高维中对 itsindex求值。
因此,计算多维数组访问的索引的顺序是不确定的

nnvyjq4y

nnvyjq4y3#

根据结构

int x; // At file scope
... and then within some function
arr[f()][x] += 1;
arr[x][f()] += 2;

编译器通过在读取x之前执行对x的调用来处理每一行将是常见的,而不管函数调用是第一索引还是第二索引。尽管直到计算了子数组地址之后才可能实际将内部索引添加到子数组指针,但编译器可在开始地址计算工作之前计算任何或所有数组下标。

相关问题