在数组索引中将变量递增 N
Increment variable by N inside array index
有人能告诉我这样的构造在 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 , %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 , %rsp
.cfi_def_cfa_offset 32
movq %fs:40, %rax
movq %rax, 8(%rsp)
xorl %eax, %eax
movabsq 5201762403, %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
那么,我可以假设这样的行为是标准行为吗?
这是正常行为。数组的名称实际上是指向数组第一个元素的指针。 array[n] 与 *(array+n)
相同
在 a[i+=N]
的情况下,表达式 i += N
将始终在访问索引之前先求值。但是您提供的示例调用 UB,因为您的示例数组仅包含两个元素,因此您正在访问数组的边界。
本身array[argc += 7]
就可以,argc + 7
的结果将用作array
的索引。
但是,在您的示例中,array
只有 2 个元素,并且 argc
永远不会为负,因此您的代码将始终由于数组访问越界而导致 UB。
您的情况显然是未定义的行为,因为您将出于以下原因超出数组界限:
首先,表达式 array[argc += 7]
等于 *((array)+(argc+=7))
,操作数的值将在计算 +
之前计算(参见 here); Operator +=
is an assignment (and not a side effect), and the value of an assignment is the result of argc
(in this case) after the assignment (cf. here)。因此,+=7
对下标有效;
其次,argc
在C++中被定义为永不为负(参见here);所以 argc += 7
将永远是 >=7
(或者在非常不现实的场景中有符号整数溢出,但仍然是 UB)。
因此,UB。
有人能告诉我这样的构造在 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 , %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 , %rsp
.cfi_def_cfa_offset 32
movq %fs:40, %rax
movq %rax, 8(%rsp)
xorl %eax, %eax
movabsq 5201762403, %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
那么,我可以假设这样的行为是标准行为吗?
这是正常行为。数组的名称实际上是指向数组第一个元素的指针。 array[n] 与 *(array+n)
相同在 a[i+=N]
的情况下,表达式 i += N
将始终在访问索引之前先求值。但是您提供的示例调用 UB,因为您的示例数组仅包含两个元素,因此您正在访问数组的边界。
本身array[argc += 7]
就可以,argc + 7
的结果将用作array
的索引。
但是,在您的示例中,array
只有 2 个元素,并且 argc
永远不会为负,因此您的代码将始终由于数组访问越界而导致 UB。
您的情况显然是未定义的行为,因为您将出于以下原因超出数组界限:
首先,表达式 array[argc += 7]
等于 *((array)+(argc+=7))
,操作数的值将在计算 +
之前计算(参见 here); Operator +=
is an assignment (and not a side effect), and the value of an assignment is the result of argc
(in this case) after the assignment (cf. here)。因此,+=7
对下标有效;
其次,argc
在C++中被定义为永不为负(参见here);所以 argc += 7
将永远是 >=7
(或者在非常不现实的场景中有符号整数溢出,但仍然是 UB)。
因此,UB。