GCC左移溢出 [英] GCC left shift overflow

查看:165
本文介绍了GCC左移溢出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面的小程序很别扭使用GCC 4.2.1版(苹果公司建立5664)在Mac上。

 的#include<&stdio.h中GT;诠释主(){
        INT X = 1&所述;&下; 32;
        诠释Y = 32;
        INT Z = 1<< ÿ;
        的printf(X:%D,Z数:%d \\ n,X,Z);
}

其结果为x:0,Z:1。
结果任何想法,为什么X和Z值是不同的?
结果非常感谢。


解决方案

简短的回答:英特尔处理器口罩移位数到5位(最多31个)。换言之,实际执行的移位是(32 | 31)= 0位(无变化)

同样的结果出现使用Linux 32位PC上的gcc。

我组装的这个计划更短的版本,因为我被为什么要导致一个非零值在所有32位的左移不解:

  INT的main(){
    诠释Y = 32;
    unsigned int类型Z = 1<< ÿ;
    unsigned int类型K = 1;
    K<< = Y;
    的printf(Z:%U,K:%U \\ N,Z,K);
}

使用命令的gcc -o -Wall -S A.S deleteme.c (评论都是我自己)

..

 主:
莱亚尔4(%ESP),ECX%
和L $ -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%;在CX寄存器32
MOVL $ 1,%eax中; AX = 1
萨勒%CL,%eax中; AX&所述;&下; = 32(32)
MOVL%eax中,-12(%EBP); Z = AX
MOVL $ 1,-8(%EBP); K = 1
MOVL -16(%EBP),ECX%; CX = Y = 32
萨勒%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)
调用printf
ADDL $ 36%ESP
popl%ECX
popl%EBP
莱亚尔-4(ECX%),ESP%
RET

好了,所以这是什么意思?这是该指令是困扰我:

 萨勒%CL,-8(%EBP); K&所述;&下; = CX(32)

显然氏EM>是的移位32位离开了。

您已经得到了我 - 它使用萨勒指令,它是一个算术移位。我不知道为什么32结果在初始位置位重新出现旋转这一点。我最初的猜测是,该处理器进行优化,执行在一个时钟周期内该指令 - 这意味着,超过31的任何变化将被视为一个不在乎。不过我很好奇,找到这个问题的答案,因为我期望的旋转应导致脱落的数据类型的左端的所有位。

我找到了一个链接到 http://faydoc.tripod.com/cpu/sal.htm 这解释说,移位计数(在CL寄存器)屏蔽到5位。这意味着,如果试图以32比特移位的实际移位执行将是零位(即没有改变)。有答案!

The following little program is very awkward using GCC version 4.2.1 (Apple Inc. build 5664) on a Mac.

#include <stdio.h>

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

The result is x:0, z: 1.
Any idea why the values of x and z are different?
Thanks a lot.

解决方案

Short answer: the Intel processor masks the shift count to 5 bits (maximum 31). In other words, the shift actually performed is ( 32 | 31 ) = 0 bits (no change).

The same result appears using gcc on a Linux 32-bit PC.

I assembled a shorter version of this program because I was puzzled by why a left shift of 32 bits should result in a non-zero value at all:

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 the command gcc -Wall -o a.s -S deleteme.c (comments are my own)

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

Ok so what does this mean? It's this instruction that puzzles me:

sall    %cl, -8(%ebp)   ; k <<= CX(32)

Clearly k is being shifted left by 32 bits.

You've got me - it's using the sall instruction which is an arithmetic shift. I don't know why rotating this by 32 results in the bit re-appearing in the initial position. My initial conjecture would be that the processor is optimised to perform this instruction in one clock cycle - which means that any shift by more than 31 would be regarded as a don't care. But I'm curious to find the answer to this because I would expect that the rotate should result in all bits falling off the left end of the data type.

I found a link to http://faydoc.tripod.com/cpu/sal.htm which explains that the shift count (in the CL register) is masked to 5 bits. This means that if you tried to shift by 32 bits the actual shift performed would be by zero bits (i.e. no change). There's the answer!

这篇关于GCC左移溢出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆