【问题标题】:Increment variable by N inside array index在数组索引内按 N 递增变量
【发布时间】: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增量之后。

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


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    a[i+=N] 的情况下,表达式i += N 总是在访问索引之前首先被评估。但是您提供的示例调用了 UB,因为您的示例数组仅包含两个元素,因此您正在访问数组的范围之外。

    【讨论】:

    • 提供的 argc 小于 -7 或大于 -6(但是是的,这可能是问题所在)。
    • @PeterA.Schneider argc 不应为负数,除非在 main 内的其他地方修改。
    • 你明白了——它是由运行时设置的,我不知道如何让它在main 看来是负面的。也许通过提供 INT_MAX+7 命令行参数,但恐怕在此之前的某个地方对命令行长度有限制。
    【解决方案3】:

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

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

    其次,argc 在 C++ 中被定义为永远不会为负(参见here);所以argc += 7 将永远是&gt;=7(或者在非常不切实际的场景中是有符号整数溢出,但仍然是UB)。

    因此,UB。

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-07
        • 2021-09-30
        • 2013-01-27
        • 2012-04-17
        相关资源
        最近更新 更多