C++ 它是g+中的一个bug+;?

C++ 它是g+中的一个bug+;?,c++,gcc,C++,Gcc,为什么我会得到这些结果?如果您的int长度为16位,则您正在运行未定义的行为,并且任何一个结果都是“OK” 将N位整数向左或向右移动N个或多个位位置会导致未定义的行为 由于这种情况发生在32位整数上,因此这是编译器中的一个错误。看起来gcc反转了移位并将其应用到另一端,我猜这是一个错误 在C(而不是C++)中也会发生同样的事情,并且C翻译成asm更容易阅读,所以我在这里使用C;此外,我还减少了测试用例(删除模板和k数组)。 foo()是原始的buggy f()函数,foo1()是foo()在gc

为什么我会得到这些结果?

如果您的
int
长度为16位,则您正在运行未定义的行为,并且任何一个结果都是“OK”

将N位整数向左或向右移动N个或多个位位置会导致未定义的行为


由于这种情况发生在32位整数上,因此这是编译器中的一个错误。

看起来gcc反转了移位并将其应用到另一端,我猜这是一个错误

在C(而不是C++)中也会发生同样的事情,并且C翻译成asm更容易阅读,所以我在这里使用C;此外,我还减少了测试用例(删除模板和k数组)。 foo()是原始的buggy f()函数,foo1()是foo()在gcc中的行为,但不应该是这样,bar()显示了除了指针读取之外foo()应该是什么样子

我使用64位,但32位是相同的,除了参数处理和查找k

g++ a.cpp && ./a.out
1
0
#包括
#包括
uint32_t k=17;
char foo(uint8_t*数据){
返回*数据<(uint8_t)(1k)<1;
/*
movzbl(%rdi),%eax
移动k(%rip),%ecx
shrl%cl,%eax
testl%eax,%eax
sete%al
ret
*/
}
字符条(uint8_t数据){

返回数据<(uint8_t)(1我很确定我们在这里讨论的是未定义的行为-将不适合新值大小的“大”整数转换为较小的值,据我所知是未定义的。131072肯定不适合uint8

尽管查看生成的代码,我还是认为它可能不太正确,因为它是“sete”而不是“setb”?这在我看来确实很可疑

如果我将表达式转过来:

#include <stdint.h>
#include <stdio.h>

uint32_t k = 17;
char foo(uint8_t *data) {
    return *data < (uint8_t)(1<<k);
/*
with gcc -O3 -S: (gcc version 4.7.2 (Debian 4.7.2-5))
    movzbl  (%rdi), %eax
    movl    k(%rip), %ecx
    shrb    %cl, %al
    testb   %al, %al
    sete    %al
    ret
*/
}
char foo1(uint8_t *data) {
    return (((uint32_t)*data) >> k) < 1;
/*
    movzbl  (%rdi), %eax
    movl    k(%rip), %ecx
    shrl    %cl, %eax
    testl   %eax, %eax
    sete    %al
    ret
*/
}
char bar(uint8_t data) {
    return data < (uint8_t)(1<<k);
/*
    movl    k(%rip), %ecx
    movl    $1, %eax
    sall    %cl, %eax
    cmpb    %al, %dil
    setb    %al
    ret
*/
}

int main() {
    uint8_t v = 0;
    printf("All should be 0: %i %i %i\n", foo(&v), foo1(&v), bar(v));
    return 0;
}

return(T)(1这里还有一些数据点:

基本上,它看起来像是gcc进行了优化(即使在-O标志关闭而-g打开的情况下):

其中[variable]需要是数组访问

我想这里的优点是它不必将文本1加载到寄存器中,这样可以保存1个寄存器

以下是数据点:

  • 将1更改为大于1的数字将强制它实现正确的版本
  • 将任何变量更改为文字将强制它实现正确的版本
  • 将[variable]更改为非数组访问将强制它实现正确的版本

  • [variable]>(键入cast)(1)这不是g++中的错误,只是您不知道这应该做什么。问题是正确的。为什么会这样downvotes@Some1.Kill.The.DJ当前位置因为这表明询问者缺乏努力。至少,他本可以(应该)这样做说明了他期望的结果,以及他期望这些结果的原因。@ZdeslavVojkovic如果有人在这里提交了PR:这发生在一个32位int的平台上的GCC上。@interjay然后,除非我遗漏了什么,否则它看起来像一个bug。我认为GCC中有一个bug,这篇文章没有回答这个问题。@ouah编辑了答案。最后是一个错误在这种情况下,“过早优化是万恶之源”这句格言确实适用!如果您想要比asm更容易阅读的东西,请尝试gcc中的-fdump tree all选项。根据定义,强制转换为未签名的较小类型是很好的。(并且应该做我们期望的事情)我注意到您的代码说“sete”同样。我还没有深入研究它,但对我来说似乎不太正确。生成的代码相当于
    测试eax,eax;setz al
    ,这看起来是返回
    x==0
    的合理方法,对于
    无符号int
    而言,它与
    x<1
    相同。对我来说似乎没问题。是的,但x<1与x不同<(1 x<(1>k)<1如果您错过了它,PR会在gcc源代码中为您提供这一优化发生的确切行…如果lhs是无符号的,而rhs是(强制转换),则会发生<和>=的优化1@Marglisse,它非常具体,我不禁想知道是否有聪明的人认为这是一个聪明的优化,可以在最需要的时候保存寄存器。否则,为什么非数组访问不会发生这种情况?PR是什么?我正在查看错误报告,但没有看到它。我尝试替换数据[0]使用局部变量d,这实际上使它起作用。还尝试将变量更改为文字,这也起作用。这种情况确实发生在非数组中,请不要这样粗鲁的评论。这段代码工作正常(d没有得到优化):#include int main(void){int a,s=8;int d=0;a=d<(无符号字符)(1)也将int d=0更改为无符号字符d=0;同样有效。
    #include <stdint.h>
    #include <stdio.h>
    
    uint32_t k = 17;
    char foo(uint8_t *data) {
        return *data < (uint8_t)(1<<k);
    /*
    with gcc -O3 -S: (gcc version 4.7.2 (Debian 4.7.2-5))
        movzbl  (%rdi), %eax
        movl    k(%rip), %ecx
        shrb    %cl, %al
        testb   %al, %al
        sete    %al
        ret
    */
    }
    char foo1(uint8_t *data) {
        return (((uint32_t)*data) >> k) < 1;
    /*
        movzbl  (%rdi), %eax
        movl    k(%rip), %ecx
        shrl    %cl, %eax
        testl   %eax, %eax
        sete    %al
        ret
    */
    }
    char bar(uint8_t data) {
        return data < (uint8_t)(1<<k);
    /*
        movl    k(%rip), %ecx
        movl    $1, %eax
        sall    %cl, %eax
        cmpb    %al, %dil
        setb    %al
        ret
    */
    }
    
    int main() {
        uint8_t v = 0;
        printf("All should be 0: %i %i %i\n", foo(&v), foo1(&v), bar(v));
        return 0;
    }
    
    return (T)(1<<k[i])  >  data[0];
    
        [variable] < (type-cast)(1 << [variable2])
    
        ((type-cast)[variable] >> [variable2]) == 0
    
        [variable] >= (type-cast)(1 << [variable2])
    
        ((type-cast)[variable] >> [variable2]) != 0
    
        #include <stdio.h>
    
        int main(void)
        {
            int a, s = 8;
            unsigned char data[1] = {0};
    
            a = data[0] < (unsigned char) (1 << s);
            printf("%d\n", a);
    
            return 0;
        }
    
         .globl main
                .type   main, @function
        main:
        leal    4(%esp), %ecx
        andl    $-16, %esp
        pushl   -4(%ecx)
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %ecx
        subl    $8, %esp
        pushl   $1                ***** seems it already precomputed the result to be 1
        pushl   $.LC0
        pushl   $1
        call    __printf_chk
        xorl    %eax, %eax
        movl    -4(%ebp), %ecx
        leave
        leal    -4(%ecx), %esp
        ret
    
        .globl main
                .type   main, @function
        main:
        leal    4(%esp), %ecx
        andl    $-16, %esp
        pushl   -4(%ecx)
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %ebx
        pushl   %ecx
        subl    $16, %esp
        movl    $8, -12(%ebp)
        movb    $0, -17(%ebp)
        movb    -17(%ebp), %dl
        movl    -12(%ebp), %eax
        movb    %dl, %bl
        movb    %al, %cl
        shrb    %cl, %bl                      ****** (unsigned char)data[0] >> s => %bl
        movb    %bl, %al                              %bl => %al
        testb   %al, %al                              %al = 0?
        sete    %dl
        movl    $0, %eax
        movb    %dl, %al
        movl    %eax, -16(%ebp)
        movl    $.LC0, %eax
        subl    $8, %esp
        pushl   -16(%ebp)
        pushl   %eax
        call    printf
        addl    $16, %esp
        movl    $0, %eax
        leal    -8(%ebp), %esp
        addl    $0, %esp
        popl    %ecx
        popl    %ebx
        popl    %ebp
        leal    -4(%ecx), %esp
        ret