Инкремент переменной на N внутри индекса массива - PullRequest
0 голосов
/ 15 января 2019

Может кто-нибудь сказать мне, действительно ли такая конструкция допустима (т.е. не 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 ]

0 голосов
/ 15 января 2019

В вашем случае явно неопределенное поведение, так как вы превысите границы массива по следующим причинам:

Во-первых, выражение array[argc += 7] равно *((array)+(argc+=7)), и значения операндов будут оцениваться до вычисления + (ср. здесь ); Оператор += является назначением (а не побочным эффектом), а значение присвоения является результатом argc (в данном случае) после присвоения (см. здесь ). Следовательно, +=7 становится эффективным для подписки;

Во-вторых, argc определен в C ++ как никогда не отрицательный (ср. здесь ); Таким образом, argc += 7 всегда будет >=7 (или целочисленным переполнением со знаком в очень нереалистичном сценарии, но все же в UB).

Следовательно, UB.

0 голосов
/ 15 января 2019

В случае a[i+=N] выражение i += N всегда будет оцениваться в первую очередь перед доступом к индексу. Но предоставленный вами пример вызывает UB, так как ваш примерный массив содержит только два элемента, и поэтому вы получаете доступ за пределы массива.

0 голосов
/ 15 января 2019

Само по себе array[argc += 7] в порядке, результат argc + 7 будет использоваться как индекс для array.

Однако в вашем примере array имеет всего 2 элемента, а argc никогда не бывает отрицательным, поэтому ваш код всегда будет приводить к UB из-за доступа к массиву за пределами допустимого диапазона.

0 голосов
/ 15 января 2019

Это нормальное поведение. Имя массива фактически является указателем на первый элемент массива. И массив [n] такой же, как * (массив + n)

...