Инкремент переменной на N внутри индекса массива
Может ли кто-нибудь сказать мне, действительно ли такая конструкция допустима (то есть не UB) в C++. Из-за этого у меня есть некоторые ошибки, и я провел пару дней, пытаясь выяснить, что там происходит.
// 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
Итак, могу ли я предположить, что такое поведение является стандартным?
4 ответа
Само собой array[argc += 7]
все в порядке, результат argc + 7
будет использоваться в качестве индекса array
,
Однако в вашем примере array
имеет только 2 элемента, и argc
никогда не бывает отрицательным, поэтому ваш код всегда будет приводить к UB из-за доступа к массиву вне границ.
В случае a[i+=N]
выражение i += N
всегда будет оцениваться в первую очередь, прежде чем получить доступ к индексу. Но приведенный вами пример вызывает UB, поскольку ваш примерный массив содержит только два элемента, и, таким образом, вы получаете доступ за пределы массива.
В вашем случае поведение явно не определено, поскольку вы превысите границы массива по следующим причинам:
Во-первых, выражение array[argc += 7]
равно *((array)+(argc+=7))
и значения операндов будут оцениваться до +
оценивается (см. здесь); оператор +=
является назначением (а не побочным эффектом), а значение присвоения является результатом argc
(в этом случае) после назначения (см. здесь). Следовательно +=7
становится эффективным для подписки;
Во-вторых, argc
определяется в C++, чтобы никогда не быть отрицательным (см. здесь); Так argc += 7
всегда будет >=7
(или целочисленное переполнение со знаком в очень нереальном сценарии, но все же UB).
Следовательно, UB.
Это нормальное поведение. Имя массива фактически является указателем на первый элемент массива. И массив [n] такой же, как *(массив +n)