在数组索引内将变量递增N

时间:2019-01-15 13:00:51

标签: c++ c++11 gcc gcc7

有人可以告诉我这种构造在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

所以,我可以假设这样的酒是一种标准的酒吗?

4 个答案:

答案 0 :(得分:4)

对于a[i+=N],在访问索引之前,将始终首先对表达式i += N求值。但是您提供的示例将调用UB,因为示例数组仅包含两个元素,因此您正在访问数组的边界。

答案 1 :(得分:4)

array[argc += 7]本身而言还可以,argc + 7的结果将用作array的索引。

但是,在您的示例中,array仅包含2个元素,并且argc永远不会为负,因此,由于越界访问数组,您的代码将始终导致UB。

答案 2 :(得分:2)

您的情况显然是未定义的行为,因为由于以下原因,您将超出数组范围:

首先,表达式array[argc += 7]等于*((array)+(argc+=7)),并且在计算+之前先对操作数的值求值(参见here);运算符+=是赋值(而不是副作用),赋值是赋值后的argc(在这种情况下)的结果(请参见here)。因此,+=7对下标有效;

第二,argc在C ++中定义为永不为负(参见here);因此,argc += 7将始终为>=7(或者在非常不现实的情况下会出现有符号整数溢出,但那时仍然是UB)。

因此,UB。

答案 3 :(得分:1)

这是正常行为。实际上数组的名称是指向数组第一个元素的指针。并且array [n]与*(array + n)

相同