来自简单 C 程序的奇怪行为

Strange behavior from a simple C program

如果我 运行 下面的代码,graph[0][0] 得到 1graph[0][1] 得到 4

换句话说,行graph[0][++graph[0][0]] = 4;1放入graph[0][0],将4放入graph[0][1]

如果有人能提供合理的解释,我将不胜感激。

我从 Visual C++ 2015 以及 Android C 编译器 (CppDriod) 观察到这一点。

static int graph[10][10];
void main(void)
{
    graph[0][++graph[0][0]] = 4;
}

让我们分解一下:

++graph[0][0]

这会预先增加 graph[0][0] 处的值,这意味着现在 graph[0][0] = 1,然后表达式的值为 1(因为那是 1 的最终值=12=]).

然后,

graph[0][/*previous expression = 1*/] = 4;

所以基本上,graph[0][1] = 4;

就是这样!现在 graph[0][0] = 1graph[0][1] = 4.

您正在 graph[0][0] 中加一,方法是 ++graph[0][0]。然后将 graph[0][1] 设置为 4。也许你想做 graph[0][graph[0][0]+1] = 4

起初你的变量 graph[10][10] 是静态的,所以它将被初始化为值 0

然后第 graph[0][++graph[0][0]] = 4 ;graph[0][0] = 0 在表达式中你只是递增 graph[0][ 的值0] 所以基本上你自己分配 graph[0][1] = 4;

请注意,您使用了预递增运算符 (++x),因此它首先递增并更改值,但如果您使用 post-递增运算符 (x++)然后graph[0][0] = 4;本身

首先让我们看看一元(前缀)增量运算符是做什么的。

The value of the operand of the prefix ++ operator is incremented. The result is the new value of the operand after incrementation.

所以,如果

graph[0][++graph[0][0]] = 4;

首先将graph[0][0]的值加1,然后在索引中使用该值

现在,graph是一个静态全局变量,由于隐式初始化,默认情况下数组中的所有成员都被初始化为0。因此,++graph[0][0]graph[0][0] 的值递增到 1,returns 将 1 的值递增。

那么,简化版的指令看起来像

graph[0][1] = 4;

因此,你得到

  • graph[0][0] 为 1
  • graph[0][1] 为 4.

此外,FWIW,main() 的推荐签名是 int main(void)

让我们把关于这个表达式的事实排列起来

graph[0][++graph[0][0]] = 4;
  • 根据 6.5.1,数组索引 ++graph[0][0] 的计算在数组元素 graph[0][++graph[0][0]] 的计算之前排序,而数组元素 graph[0][++graph[0][0]] 又在数组元素的计算之前排序整个赋值运算符。

  • ++graph[0][0]的值要求为1。请注意,这并不意味着整个预增量及其副作用必须 "happen first"。它只是意味着必须首先计算该预增量(1)的结果。 graph[0][0] 的实际修改(即将 graph[0][0]0 更改为 1)可能要晚得多。没有人知道它什么时候会发生(在语句结束之前的某个时间)。

  • 这意味着被赋值运算符修改的元素是graph[0][1]。这是 4 应该去的地方。将 4 赋值给 graph[0][1] 也是 = 运算符的副作用,它会在语句结束之前的某个时间发生。

请注意,在这种情况下,我们可以最终确定 ++ 修改 graph[0][0],而 = 修改 graph[0][1]。我们有两个未排序的副作用(这很危险),但它们作用于两个不同的对象(这使它们安全)。在这种情况下,这正是使我们免于未定义行为的原因。

但是,这取决于graph数组的初始值。如果你试试这个

graph[0][0] = -1;
graph[0][++graph[0][0]] = 4;

行为将立即变为未定义,即使表达式本身看起来相同。在这种情况下,++ 的副作用和 = 的副作用应用于同一数组元素 graph[0][0]。副作用彼此之间没有排序,这意味着行为未定义。