首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >GCC左移溢出

GCC左移溢出
EN

Stack Overflow用户
提问于 2010-10-06 18:43:07
回答 3查看 16.1K关注 0票数 26

下面的小程序在Mac上使用GCC 4.2.1版(苹果公司版本5664)是非常笨拙的。

代码语言:javascript
运行
复制
#include <stdio.h>

int main(){
        int x = 1 << 32;
        int y = 32;
        int z = 1 << y;
        printf("x:%d, z: %d\n", x, z);
}

结果是x:0, z: 1

知道为什么x和z的值不同吗?

非常感谢。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-10-06 18:47:14

简而言之: Intel处理器将移位计数屏蔽为5位(最多31位)。换句话说,实际执行的移位是32 & 31,也就是0(不变)。

在Linux32位PC上使用gcc也会得到同样的结果。

我汇编了这个程序的一个较短的版本,因为我对为什么32位的左移会导致一个非零值感到困惑:

代码语言:javascript
运行
复制
int main(){
    int y = 32;
    unsigned int z = 1 << y;
    unsigned int k = 1;
    k <<= y;
    printf("z: %u, k: %u\n", z, k);
}

..using命令gcc -Wall -o a.s -S deleteme.c (注释是我自己的)

代码语言:javascript
运行
复制
main:
leal    4(%esp), %ecx
andl    $-16, %esp
pushl   -4(%ecx)
pushl   %ebp
movl    %esp, %ebp
pushl   %ecx
subl    $36, %esp
movl    $32, -16(%ebp)  ; y = 32
movl    -16(%ebp), %ecx ; 32 in CX register
movl    $1, %eax        ; AX = 1
sall    %cl, %eax       ; AX <<= 32(32)
movl    %eax, -12(%ebp) ; z = AX
movl    $1, -8(%ebp)    ; k = 1
movl    -16(%ebp), %ecx ; CX = y = 32
sall    %cl, -8(%ebp)   ; k <<= CX(32)
movl    -8(%ebp), %eax  ; AX = k
movl    %eax, 8(%esp)
movl    -12(%ebp), %eax
movl    %eax, 4(%esp)
movl    $.LC0, (%esp)
call    printf
addl    $36, %esp
popl    %ecx
popl    %ebp
leal    -4(%ecx), %esp
ret

好的,这意味着什么呢?让我困惑的是这条指令:

代码语言:javascript
运行
复制
sall    %cl, -8(%ebp)   ; k <<= CX(32)

显然,k被左移了32位。

你可以理解我--它使用的是一个arithmetic shiftsall指令。我不知道为什么旋转32会导致位重新出现在初始位置。我最初的猜测是,处理器被优化为在一个时钟周期内执行这条指令-这意味着任何超过31的移位都将被视为无关。但我很好奇地想找到这个问题的答案,因为我期望旋转应该会导致数据类型左端的所有位都掉下来。

我找到了一个指向http://faydoc.tripod.com/cpu/sal.htm的链接,它解释了移位计数(在CL寄存器中)被屏蔽为5位。这意味着,如果您尝试移位32位,实际执行的移位将是零位(即没有变化)。这就是答案!

票数 20
EN

Stack Overflow用户

发布于 2010-10-06 20:51:06

C99标准规定,将一个数按操作数的位(或更多位)的宽度移位的结果是未定义的。为什么?

这使得编译器可以为特定的架构创建最高效的代码。例如,i386移位指令使用5位宽的字段表示要将32位操作数移位的位数。C99标准允许编译器简单地获取移位计数的底部五位并将它们放入字段中。显然,这意味着32位的移位(二进制= 100000 )因此与0的移位相同,因此结果将是左操作数不变。

不同的CPU架构可以使用更宽的位域,例如32位。编译器仍然可以将移位计数直接放入字段中,但这次结果将为0,因为32位的移位会将左操作数中的所有位移出。

如果C99定义这些行为中的一种或另一种是正确的,则英特尔的编译器必须对移位计数过大进行特殊检查,或者非i386的编译器必须屏蔽移位计数。

原因是

代码语言:javascript
运行
复制
   int x = 1 << 32;

代码语言:javascript
运行
复制
   int z = 1 << y;

给出不同的结果是因为第一个计算是一个常量表达式,可以完全由编译器执行。编译器必须使用64位算术计算常量表达式。第二个表达式由编译器生成的代码计算。因为y和z的类型都是int,所以代码使用32位宽的int生成计算(int在i386和x86_64上都是32位,在苹果上是gcc )。

票数 7
EN

Stack Overflow用户

发布于 2014-02-07 16:47:54

在我看来,如果sizeof(Int)为==4,"int =ysize32;“就没有意义了。

但我也遇到了类似的问题:

长y= ...long x=y << 32;

在那里我得到了一个警告“警告:左移位计数>=宽度的类型”,即使在目标上的sizeof(长)是8。我通过执行以下操作消除了警告:

long x= (y << 16) << 16;

这似乎起到了作用。

在64位架构上,没有任何警告。在32位架构上有。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3871650

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档