使用 gcc 进行按位移位的意外行为

发布于 2024-12-01 20:07:38 字数 658 浏览 2 评论 0原文

我有一个这样的测试程序:

int main()
{
    unsigned n = 32;

    printf("ans << 32 = 0x%X\n", (~0x0U) << 32);
    printf("ans >> 32 = 0x%X\n", (~0x0U) >> 32);

    printf("ans << n(32) = 0x%X\n", (~0x0U) << n);
    printf("ans >> n(32) = 0x%X\n", (~0x0U) >> n);

    return 0;
}  

它产生以下输出:

ans << 32 = 0x0  ... (1)  
ans >> 32 = 0x0  ... (2)  
ans << n(32) = 0xFFFFFFFF  ... (3)  
ans >> n(32) = 0xFFFFFFFF  ... (4)   

我期望 (1) 和 (3) 相同,以及 (2) 和 (4) 相同。

使用 gcc 版本:gcc.real (Ubuntu 4.4.1-4ubuntu9) 4.4.1

发生了什么?

I have a test program like this:

int main()
{
    unsigned n = 32;

    printf("ans << 32 = 0x%X\n", (~0x0U) << 32);
    printf("ans >> 32 = 0x%X\n", (~0x0U) >> 32);

    printf("ans << n(32) = 0x%X\n", (~0x0U) << n);
    printf("ans >> n(32) = 0x%X\n", (~0x0U) >> n);

    return 0;
}  

It produces the following output:

ans << 32 = 0x0  ... (1)  
ans >> 32 = 0x0  ... (2)  
ans << n(32) = 0xFFFFFFFF  ... (3)  
ans >> n(32) = 0xFFFFFFFF  ... (4)   

I was expecting (1) and (3) to be the same, as well as (2) and (4) to be the same.

Using gcc version: gcc.real (Ubuntu 4.4.1-4ubuntu9) 4.4.1

What is happening?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

月朦胧 2024-12-08 20:07:38

根据 C 标准,第 6.5.7.3 条:

6.5.7 按位移位运算符
(...) 如果值
右操作数为负数或大于或等于宽度
对于提升的左操作数,行为未定义。

您的编译器应该警告您这一点:

$ gcc shift.c -o shift -Wall
shift.c: In function ‘main’:
shift.c:5:5: warning: left shift count >= width of type [enabled by default]
shift.c:6:5: warning: right shift count >= width of type [enabled by default]

如果您查看 gcc 正在生成的 汇编代码,您会看到它是实际上在编译时计算前两个结果。简化:

main:
    movl    $0, %esi
    call    printf

    movl    $0, %esi
    call    printf

    movl    -4(%rbp), %ecx  ; -4(%rbp) is n
    movl    $-1, %esi
    sall    %cl, %esi       ; This ignores all but the 5 lowest bits of %cl/%ecx
    call    printf

    movl    -4(%rbp), %ecx
    movl    $-1, %esi
    shrl    %cl, %esi
    call    printf

Shifting by the size of the type is undefined behavior, according to the C standard, § 6.5.7.3:

6.5.7 Bitwise shift operators
(...) If the value of
the right operand is negative or is greater than or equal to the width
of the promoted left operand, the behavior is undefined.

Your compiler should warn you about this:

$ gcc shift.c -o shift -Wall
shift.c: In function ‘main’:
shift.c:5:5: warning: left shift count >= width of type [enabled by default]
shift.c:6:5: warning: right shift count >= width of type [enabled by default]

If you look at the assembler code gcc is generating, you'll see it is actually calculating the first two results at compilation time. Simplified:

main:
    movl    $0, %esi
    call    printf

    movl    $0, %esi
    call    printf

    movl    -4(%rbp), %ecx  ; -4(%rbp) is n
    movl    $-1, %esi
    sall    %cl, %esi       ; This ignores all but the 5 lowest bits of %cl/%ecx
    call    printf

    movl    -4(%rbp), %ecx
    movl    $-1, %esi
    shrl    %cl, %esi
    call    printf
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文