int arr = {0}; int value = arr ++]; 值= 1?

今天我来了Eric Lippert的一篇文章,他试图清除运营商优先级和评估顺序之间的神话。 最后有两个代码片段让我困惑,这是第一个片段:

int[] arr = {0}; int value = arr[arr[0]++]; 

现在,当我考虑变量值的值时,我只是将其计算为1。 这就是我认为它的工作方式。

  1. 首先将arr声明为int数组,其中包含一个项目; 这个项目的值是0。
  2. 在这种情况下,第二个得到arr [0] –0的值。
  3. 第三个得到arr的值[步骤2的值](仍为0) – 再次设置arr [0] – 仍然为0。
  4. 第四步将步骤3(0)的值赋给变量值。 –value = 0现在
  5. 添加到步骤2的值1 – 现在arr [0] = 1。

显然这是错误的。 我试图在c#规范中搜索关于何时实际发生增量的一些明确陈述,但没有找到任何增量。
第二个片段来自Eric关于该主题的博客文章的评论:

  int[] data = { 11, 22, 33 }; int i = 1; data[i++] = data[i] + 5; 

现在这是我认为该程序将执行的方式 – 在声明数组并为i分配1之后。 [跟我一起]

  1. 获取数据[i] –1
  2. 将值5 – 6添加到步骤1的值
  3. 分配给data [i](仍为1)步骤2的值–data [i] = 6
  4. 增量i – i = 2

根据我的理解,这个数组现在应该包含值{11,27,33}。 但是,当我循环打印我得到的数组值时:{11,38,33}。 这意味着在取消引用数组之前发生了增量!
怎么会? 这个post增量不应该发布吗? 即发生在其他一切之后。
我想念的是什么人?

后增量操作作为评估整体表达的一部分发生。 这是在评估值之后但在评估任何其他表达式之前发生的副作用。

换句话说,对于任何表达式E,E ++(如果合法)表示类似(伪代码):

 T tmp = E; E += 1; return tmp; 

在评估其他任何东西之前,这都是评估E ++的一部分。

有关更多详细信息,请参阅C#3.0规范的第7.5.9节。


另外,对于将LHS归类为变量的分配操作(如本例所示),在评估RHS 之前评估LHS。

所以在你的例子中:

 int[] data = { 11, 22, 33 }; int i = 1; data[i++] = data[i] + 5; 

相当于:

 int[] data = { 11, 22, 33 }; int i = 1; // Work out what the LHS is going to mean... int index = i; i++; // We're going to assign to data[index], ie data[1]. Now i=2. // Now evaluate the RHS int rhs = data[i] + 5; // rhs = data[2] + 5 == 38 // Now assign: data[index] = rhs; 

规范的相关部分是第7.16.1节(C#3.0规范)。

对于第一个片段,序列是:

  1. 如你所述声明arr:
  2. 检索arr [0]的值,即0
  3. 将arr [0]的值增加到1。
  4. 检索arr [(#2的结果)]的值,即arr [0],(每#3)为1。
  5. 存储导致value
  6. 值= 1

对于第二个片段,评估仍然是从左到右。

  1. 我们在哪里存储结果? 在data [i ++]中,它是数据[1],但现在i = 2
  2. 我们加什么? data [i] + 5,现在是数据[2] + 5,即38。

缺少的部分是“post”并不意味着“在其他所有事情之后”。 它只是意味着“在我检索到该变量的当前值之后立即”。 在“一行代码”中间发生的后增量是完全正常的。

 data[i++] // => data[1], then i is incremented to 2 data[1] = data[2] + 5 // => 33 + 5 

我希望后增量运算符在使用其值后递增变量。 在这种情况下,变量在第二次引用变量之前递增。

如果不是这样,你可以写

 data[i++] = data[i++] + data[i++] + data[i++] + 5 

如果它就像你说的那样,你可以删除增量运算符,因为它在我报告的指令中实际上没有做任何事情。

您必须分三个步骤考虑作业:

  1. 评估左侧(=获取应存储值的地址)
  2. 评估右侧
  3. 将步骤2中的值分配给步骤1中的内存位置。

如果你有类似的东西

 A().B = C() 

然后首先运行A(),然后运行C(),然后运行属性设置器B.

从本质上讲,你必须将你的陈述视为

 StoreInArray(data, i++, data[i] + 5); 

原因可能是某些编译器将i ++优化为++ i。 大多数情况下,最终结果是相同的,但在我看来,这是编译器错误的罕见情况之一。

我现在无法访问Visual Studio来确认这一点,但尝试禁用代码优化并查看结果是否保持不变。