首页 > 解决方案 > C 中数组索引的求值顺序(相对于表达式)

问题描述

看这段代码:

static int global_var = 0;

int update_three(int val)
{
    global_var = val;
    return 3;
}

int main()
{
    int arr[5];
    arr[global_var] = update_three(2);
}

哪个数组条目得到更新?0 还是 2?

C 的规范中是否有部分指示在这种特殊情况下操作的优先级?

标签: clanguage-lawyerorder-of-execution

解决方案


左右操作数的顺序

要在 中执行赋值arr[global_var] = update_three(2),C 实现必须评估操作数,并且作为副作用,更新左操作数的存储值。C 2018 6.5.16(关于赋值)第 3 段告诉我们左右操作数没有排序:

操作数的评估是无序的。

这意味着 C 实现可以自由地首先计算左值 arr[global_var](通过“计算左值”,我们的意思是弄清楚这个表达式指的是什么),然后计算update_three(2),最后将后者的值分配给前者;或者先求update_three(2)值,然后计算左值,然后将前者赋给后者;或以update_three(2)某种混合方式评估左值,然后将右值分配给左左值。

在所有情况下,将值分配给左值必须最后,因为 6.5.16 3 还说:

…更新左操作数的存储值的副作用是在左操作数和右操作数的值计算之后排序的……</p>

测序违规

有些人可能会考虑由于global_var违反 6.5 2 使用和单独更新它而导致的未定义行为,其中说:

如果标量对象的副作用相对于同一标量对象的不同副作用或使用同一标量对象的值的值计算是未排序的,则行为未定义......</p>

许多 C 实践者都非常熟悉,诸如此类的表达式的行为x + x++并没有由 C 标准定义,因为它们既使用 的值,x又在同一个表达式中单独修改它而没有排序。但是,在这种情况下,我们有一个函数调用,它提供了一些排序。在函数调用中global_var使用并更新。arr[global_var]update_three(2)

6.5.2.2 10 告诉我们在调用函数之前有一个序列点:

在函数指示符和实际参数的评估之后但在实际调用之前有一个序列点……</p>

在函数内部,global_var = val;是一个完整的表达式3in也是如此return 3;,根据 6.8 4:

完整表达式是不属于另一个表达式的表达式,也不是声明符或抽象声明符的一部分……</p >

然后在这两个表达式之间有一个序列点,再次按照 6.8 4:

... 在完整表达式的评估和要评估的下一个完整表达式的评估之间存在一个序列点。

因此,C 实现可能arr[global_var]先求值,然后进行函数调用,在这种情况下,它们之间有一个序列点,因为在函数调用之前有一个,或者它可能global_var = val;在函数调用中求值然后arr[global_var],在这种情况下,存在它们之间有一个序列点,因为在完整表达式之后有一个。所以行为是未指定的——可以先评估这两件事中的任何一个——但它不是未定义的。


推荐阅读