来自简单 C 程序的奇怪行为
Strange behavior from a simple C program
如果我 运行 下面的代码,graph[0][0]
得到 1
而 graph[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] = 1
和 graph[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]
。副作用彼此之间没有排序,这意味着行为未定义。
如果我 运行 下面的代码,graph[0][0]
得到 1
而 graph[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] = 1
和 graph[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]
为 1graph[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]
。副作用彼此之间没有排序,这意味着行为未定义。