【发布时间】:2019-06-09 11:54:52
【问题描述】:
有人能告诉我这样的构造在 C++ 中是否有效(即不是 UB)。因此,我遇到了一些段错误,并花了几天时间试图弄清楚那里发生了什么。
// Synthetic example
int main(int argc, char** argv)
{
int array[2] = {99, 99};
/*
The point is here. Is it legal? Does it have defined behaviour?
Will it increment first and than access element or vise versa?
*/
std::cout << array[argc += 7]; // Use argc just to avoid some optimisations
}
所以,我当然做了一些分析,GCC(5/7) 和 clang(3.8) 都生成相同的代码。先添加后访问。
Clang(3.8): clang++ -O3 -S test.cpp
leal 7(%rdi), %ebx
movl .L_ZZ4mainE5array+28(,%rax,4), %esi
movl $_ZSt4cout, %edi
callq _ZNSolsEi
movl $.L.str, %esi
movl $1, %edx
movq %rax, %rdi
callq _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
GCC(5/7) g++-7 -O3 -S test.cpp
leal 7(%rdi), %ebx
movl $_ZSt4cout, %edi
subq $16, %rsp
.cfi_def_cfa_offset 32
movq %fs:40, %rax
movq %rax, 8(%rsp)
xorl %eax, %eax
movabsq $425201762403, %rax
movq %rax, (%rsp)
movslq %ebx, %rax
movl (%rsp,%rax,4), %esi
call _ZNSolsEi
movl $.LC0, %esi
movq %rax, %rdi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movl %ebx, %esi
那么,我可以假设这样的行为是标准行为吗?
【问题讨论】:
-
如果你问一个关于 C++ 和 UB 的问题,那么不要标记其他语言。 C 是一种完全不同的语言,具有其他语义规则和 UB 的其他点。
-
至于您的问题,除非
argc是-6或-7,否则您将使用越界索引,那当然是UB。用于索引的表达式必须首先被完全计算,并且在 C++ 中所有的赋值变体都是简单的表达式。 -
我的问题是 a[i+=N] 是否总是先增加 i。
-
@Dmitry 当然可以;仅当您在同一表达式中的其他位置使用 argc 时才会出现问题,因为通常未定义表达式中子表达式的求值顺序(以及副作用的传播)。
-
@Dmitry 你有没有想到
argc++的语义?该表达式将具有原始的 pre- 增量值作为 argv 的索引;新值仅在下一条语句中可见。但是表达式argc += 1(或+= 7)的值是argc在增量之后。