【发布时间】:2017-12-07 09:01:50
【问题描述】:
问题
这个例子是这个问题的最小重现,遇到了代码高尔夫尝试。我不提倡任何人使用这种风格。
我知道我可能遇到了未定义的行为,我的问题是:为什么具体是这样,这对 C 和 C++ 都适用吗?
我有这个文件:
#include <stdio.h>
int main() {
int a[]={1, 0}, i=0;
i += a[i]++;
printf("i=%d a=[%d, %d]\n", i, a[0], a[1]);
return 0;
}
在 WSL 上的 Ubuntu 上使用 GCC,我得到了预期的 i=1 a=[2, 0],但使用 Visual C++,它是 i=1 a=[1, 1]
如果我这样做,问题就解决了:
delta = a[i]++;
i += delta;
虽然未指定赋值的评估顺序,但令我感到惊讶的是,a[i] 似乎在 i 的赋值之后再次评估了增量,。下面的反汇编似乎证实了这一点。
C 或 C++ 标准是否允许这样做?
背景
在 Windows 上构建命令行和输出:
>cl test.c
Microsoft (R) C/C++ Optimizing Compiler Version 19.12.25830.2 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
test.c
Microsoft (R) Incremental Linker Version 14.12.25830.2
Copyright (C) Microsoft Corporation. All rights reserved.
/out:test.exe
test.obj
声明的反汇编(使用/Zi构建):
4: i += a[i]++;
00007FF6CBBA626C movsxd rax,dword ptr [i]
00007FF6CBBA6271 mov eax,dword ptr a[rax*4]
00007FF6CBBA6275 mov ecx,dword ptr [i]
00007FF6CBBA6279 add ecx,eax
00007FF6CBBA627B mov eax,ecx
00007FF6CBBA627D mov dword ptr [i],eax
00007FF6CBBA6281 movsxd rax,dword ptr [i]
00007FF6CBBA6286 mov eax,dword ptr a[rax*4]
00007FF6CBBA628A inc eax
00007FF6CBBA628C movsxd rcx,dword ptr [i]
00007FF6CBBA6291 mov dword ptr a[rcx*4],eax
【问题讨论】:
-
对标题中提出的问题的答案仍然是“因为这个语句没有定义的行为”。我真的不明白你想从这个问题中得到什么。此语句在 C 或 C++ 中没有定义的行为。句号。试图推理它的作用是没有用的,就像我们不试图从“这个陈述是错误的”这句话中理解一样。
-
@SijmenMulder:你打破的规则是“在一个无序的步骤中读取和写入同一个变量”。我们还能说什么?!
-
@SijmenMulder:C 和 C++ 都将其归为 UB,尽管标准引用了略微不同的原因,主要集中在 C++ 中尚未定义的排序点的重新定义上(尚未)将其纳入 C 标准。
-
实际上我没有看到任何理由说代码未定义。它与
i = i + a[i]相同,这很好(增加a[0]的无序副作用并不重要,因为这不会影响i)。在所有 C 和 C++ 版本中,右侧所有操作数的评估(但不是副作用)在写入左侧之前排序 -
我投票决定重新打开,因为在我所有的努力中,我只能推断它是 MSVC 中的一个错误(据说它支持 C++11),因此不是一个骗子。 @OliverCharlesworth
标签: c++ c visual-c++