【问题标题】:Compiling with optimization gets a condition wrong使用优化编译会导致条件错误
【发布时间】:2020-06-07 07:02:37
【问题描述】:

这段代码在循环终止条件中有一个错误。 但是,我仍然不明白编译器的决定——它似乎又进入了循环。

#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[])
{
    #define ARR_SIZE 25
    int a[ARR_SIZE];
    memset (a,1,sizeof(a)); /*filling the array with non-zeros*/

    int i = 0;

    for (i=0; (a[i] != 0 && i < ARR_SIZE); i++)
    {
        printf ("i=%d a[i]=%d\n",i,a[i]);
    }
    return 0;
}

当使用-O2-O3 编译它时,循环不会在预期时终止 - 它还会在i == ARR_SIZE 时打印一行。

> gcc -O3  test_loop.c
> ./a.out
i=0 a[i]=16843009
i=1 a[i]=16843009
...
i=23 a[i]=16843009
i=24 a[i]=16843009
i=25 a[i]=32766      <=== Don't understand this one.

>  gcc -O0  test_loop.c
>  a.out
i=0 a[i]=16843009
i=1 a[i]=16843009
...
i=23 a[i]=16843009
i=24 a[i]=16843009
>

gcc 版本是这样的:gcc version 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC)

我没有看到 gcc 4.4.7-18 发生这种情况。

ARR_SIZE 的其他尺寸也不会给出相同的结果。

【问题讨论】:

    标签: c gcc optimization


    【解决方案1】:

    i == ARR_SIZE 你的条件将评估a[i] 调用UB

    for (i=0; (a[i] != 0 && i < ARR_SIZE); i++)
    //         ^^^^ Undefined Behaviour
    {
        printf ("i=%d a[i]=%d\n",i,a[i]);
    }
    

    交换条件:for (... (i &lt; ARR_SIZE &amp;&amp; a[i] != 0) ...) 以利用“短路布尔评估”。

    【讨论】:

      【解决方案2】:

      现代编译器注意到未定义的行为,他们可以以此为借口生成“意外”代码,这就是您所遇到的。见https://godbolt.org/z/SEZKBZ,你必须回到4.6.x才能让i &lt; ARR_SIZE比较出现在优化的编译代码中(实际上对于那个旧版本不是很优化):

      ...
      call    printf
      lea     eax, [rbx+1]
      mov     edx, DWORD PTR [rsp+4+rbx*4]
      cmp     eax, 24
      setle   cl
      test    edx, edx
      setne   al
      add     rbx, 1
      test    cl, al
      jne     .L3
      

      更高版本仅包含零测试:

      ...
      call    printf
      mov     edx, DWORD PTR [rsp+rbx*4]
      test    edx, edx
      jne     .L8
      

      如果您检查优化编译代码的第一部分,您会看到 memset() 调用被内联并展开,因此编译器确切地知道数组中的内容并且循环条件将从它索引(数组)在退出之前(因为里面没有零)。然后它不再关心其他条件。


      同样,如果您按照建议将代码修复为a[i] != 0 &amp;&amp; i &lt; ARR_SIZE,编译器仍然知道数组中没有零,并优化零检查,只是这一次正确代码的优化会导致正确的行为:
      call    printf
      cmp     rbx, 25
      je      .L6
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-19
        • 1970-01-01
        • 2016-05-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-22
        相关资源
        最近更新 更多