繁体   English   中英

c中的左移位

[英]Left shift bits in c

我一直在做一些关于位操作的愚蠢测试,我发现了这个问题。 我执行这段代码:

int main(){
  unsigned int i;
  for (i=1; i<34; i++)
  {
    unsigned long temp = i;
    unsigned long mul = 1;
    unsigned long val;
    unsigned long one = 1;

    // Way 1
    while (temp--)
      mul = mul << one;

    // Way 2
    val = (one<<i);

    printf(" \n 1<<%i \n mul: 0x%X , val: 0x%X\n",i, mul, val); 
  }
}

当然,我知道当我> 31时,会产生溢出。 我认为代码的两个部分(way1和way2)应该输出相同的结果。 但我得到了这个(最后):

 /* ... correct results from i=1 to i=31 ... */
 1<<30 
 mul: 0x40000000 , val: 0x40000000 

 1<<31 
 mul: 0x80000000 , val: 0x80000000 

 1<<32 
 mul: **0x0** , val: **0x1** 

 1<<33 
 mul: **0x0** , val: **0x2**

为什么,如果两个指令都是左移,程序会产生不同的输出? 似乎part way2产生了一个圆转换,但我不知道为什么,我真的认为“mul”总是得到正确的值。

我在Intel 32bits机器下编译,gcc版本4.4.7

可能是因为那是未定义的行为? 根据§6.5.7

如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为未定义。

的情况下

val = (one<<i);

i大于或等于32时,行为是未定义的。

但是,如果是的话

while (temp--)
   mul = mul << one;

对于超过32的移位,它将移零并且结果被定义(零)。

当你这样做:

val = (one<<i);

你做一个单一的左移i 如果i大于31,则会导致未定义的行为 ,这意味着结果可能会或可能不是您所期望的。

根据C标准第6.5.7.3节:

对每个操作数执行整数提升。 结果的类型是提升的左操作数的类型。 如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为未定义。

但是,当你这样做时:

while (temp--)
  mul = mul << one;

你做了1左移i倍。 这是明确定义的,因此它为您提供了您期望的价值。

此外,当您使用%lX时,您正在使用%X打印long %lX 这也会导致未定义的行为。

当我用-Wall编译你的代码时,我收到了投诉:

BASH> gcc -Wall left-shift.c 
left-shift.c: In function ‘main’:
left-shift.c:21:12: warning: format ‘%X’ expects argument of type ‘unsigned int’, but argument 3 has type ‘long unsigned int’ [-Wformat=]
     printf(" \n 1<<%i \n mul: 0x%X , val: 0x%X\n",i, mul, val); 
            ^
left-shift.c:21:12: warning: format ‘%X’ expects argument of type ‘unsigned int’, but argument 4 has type ‘long unsigned int’ [-Wformat=]

所以我把printf改成了

printf(" \n 1<<%i \n mul: 0x%lX , val: 0x%lX\n",i, mul, val);

通过此更改,“mul”和“val”显示相同的结果:

 1<<30 
 mul: 0x40000000 , val: 0x40000000

 1<<31 
 mul: 0x80000000 , val: 0x80000000

 1<<32 
 mul: 0x100000000 , val: 0x100000000

 1<<33 
 mul: 0x200000000 , val: 0x200000000

系统信息:

BASH> gcc --version
gcc (Ubuntu 5.3.1-14ubuntu2.1) 5.3.1 20160413
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
BASH> uname -a
Linux bm-pc-ubuntu 4.4.0-24-generic #43-Ubuntu SMP Wed Jun 8 19:27:37 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
BASH> lsb_release --all
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 16.04 LTS
Release:    16.04
Codename:   xenial

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM