为什么i = v [i ++]未定义? [英] Why is i = v[i++] undefined?

查看:230
本文介绍了为什么i = v [i ++]未定义?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

从C ++(C ++ 11)标准,讨论评估排序的§1.9.15是以下代码示例:

From the C++ (C++11) standard, §1.9.15 which discusses ordering of evaluation, is the following code example:

void g(int i, int* v) {
    i = v[i++]; // the behavior is undefined
}

如代码示例所示, undefined。

As noted in the code sample, the behavior is undefined.

(注意:另一个问题的答案略有不同的结构 i + i ++ 为什么a = i + i ++未定义且未指定行为可能在这里应用:答案本质上是行为未定义的历史原因,而不是不必要的,但标准似乎暗示一些调整为此未定义 - 请参见下面的立即下面。此外,该链接问题表明协议,该行为应该是未指定,而在这个问题,我问为什么行为不是明确指定

(Note: The answer to another question with the slightly different construct i + i++, Why is a = i + i++ undefined and not unspecified behaviour, might apply here: The answer is essentially that the behavior is undefined for historical reasons, and not out of necessity. However, the standard seems to imply some justification for this being undefined - see quote immediately below. Also, that linked question indicates agreement that the behavior should be unspecified, whereas in this question I am asking why the behavior is not well-specified.)

标准对未定义行为的推理如下:

The reasoning given by the standard for the undefined behavior is as follows:


如果对于标量对象的副作用不是相对于
对同一标量对象的另一个副作用,或者是使用相同标量对象的值的值计算
,行为是不确定的。

If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.

在这个例子中,我认为子表达式 i ++ 将在子表达式 v [...] 之前完全评估,并且评估的结果子表达式是 i (在增量之前),但 i 该子表达式之后的增量值已完全评估。我认为,在那一点(在子表达式 i ++ 已经被完全评估之后),评估 v [...] 发生,其后是赋值 i = ...

In this example I would think that the subexpression i++ would be completely evaluated before the subexpression v[...] is evaluated, and that the result of evaluation of the subexpression is i (before the increment), but that the value of i is the incremented value after that subexpression has been completely evaluated. I would think that at that point (after the subexpression i++ has been completely evaluated), the evaluation v[...] takes place, followed by the assignment i = ....

因此, code> i 是无意义的,但我认为应该定义

Therefore, although the incrementing of i is pointless, I would nonetheless think that this should be defined.

这个未定义的行为?

推荐答案

我要设计一个病理计算机 1 。它是一个多核,高延迟,单线程系统,具有以字节级指令操作的线程连接。所以你要求某事发生,然后计算机(在其自己的线程或任务)运行字节级指令集,并且一定数量的周期后操作完成。

I'm going to design a pathological computer1. It is a multi-core, high-latency, single-thread system with in-thread joins that operates with byte-level instructions. So you make a request for something to happen, then the computer runs (in its own "thread" or "task") a byte-level set of instructions, and a certain number of cycles later the operation is complete.

同时,主线程继续执行:

Meanwhile, the main thread of execution continues:

void foo(int v[], int i){
  i = v[i++];
}

变成伪代码:

input variable i // = 0x00000000
input variable v // = &[0xBAADF00D, 0xABABABABAB, 0x10101010]
task get_i_value: GET_VAR_VALUE<int>(i)
reg indx = WAIT(get_i_value)
task write_i++_back: WRITE(i, INC(indx))
task get_v_value: GET_VAR_VALUE<int*>(v)
reg arr = WAIT(get_v_value)
task get_v[i]_value = CALC(arr + sizeof(int)*indx)
reg pval = WAIT(get_v[i]_value)
task read_v[i]_value = LOAD_VALUE<int>(pval)
reg got_value = WAIT(read_v[i]_value)
task write_i_value_again = WRITE(i, got_value)
(discard, discard) = WAIT(write_i++_back, write_i_value_again)

'等待 write_i ++ _ back 直到最后,与我在等待的同时 write_i_value_again 值I从 v [] 加载)。

So you'll notice that I didn't wait on write_i++_back until the very end, the same time as I was waiting on write_i_value_again (which value I loaded from v[]). And, in fact, those writes are the only writes back to memory.

想象一下,如果写入内存是这个电脑设计中真的很慢的部分,他们就会成堆了

Imagine if write to memory are the really slow part of this computer design, and they get batched up into a queue of things that get processed by a parallel memory modifying unit that does things on a per-byte basis.

因此, write(i)可以通过一个并行存储器修改单元来处理。 ,0x00000001) write(i,0xBAADF00D)执行无序并行。

So the write(i, 0x00000001) and write(i, 0xBAADF00D) execute unordered and in parallel. Each gets turned into byte-level writes, and they are randomly ordered.

我们最终写入 0x00 ,然后 0xBA 到高字节,然后 0xAD 0x00 下一个字节,然后 0xF0 0x00 到最后一个字节,最后 0x0D 0x01 到低字节。在i中的结果值是 0xBA000001 ,这很少有人期望,但是将是一个有效的结果到你的未定义的操作。

We end up writing 0x00 then 0xBA to the high byte, then 0xAD and 0x00 to the next byte, then 0xF0 0x00 to the next byte, and finally 0x0D 0x01 to the low byte. The resulting value in i is 0xBA000001, which few would expect, yet would be a valid result to your undefined operation.

现在,我在那里做的是导致一个未指定的值。我们没有崩溃系统。但是编译器可以自由地使它完全未定义 - 也许发送两个这样的请求到内存控制器在相同地址在同一批次的指令实际崩溃系统。这仍然是一个有效的方式来编译C ++和一个有效的执行环境。

Now, all I did there was result in an unspecified value. We haven't crashed the system. But the compiler would be free to make it completely undefined -- maybe sending two such requests to the memory controller for the same address in the same batch of instructions actually crashes the system. That would still be a "valid" way to compile C++, and a "valid" execution environment.

记住,这是一种语言,将指针的大小限制为8位仍然是有效的执行环境。

Remember, this is a language where restricting the size of pointers to 8 bits is still a valid execution environment. C++ allows for compiling to rather wonkey targets.

1 :正如@ SteveJessop在下面的评论中所提到的,这个病态电脑表现为很像现代的台式电脑,直到你进入字节级操作。非原子 int 由一个CPU写入并不是在一些硬件上罕见的(如当 int isn没有对齐CPU需要对齐的方式)。

1: As noted in @SteveJessop's comment below, the joke is that this pathological computer behaves a lot like a modern desktop computer, until you get down to the byte-level operations. Non-atomic int writing by a CPU isn't all that rare on some hardware (such as when the int isn't aligned the way the CPU wants it to be aligned).

这篇关于为什么i = v [i ++]未定义?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆