18

在下面的表达式中,左移运算的结果被赋值给变量i

int i;
i = 7 << 32;
printf("i = %d\n",i);

在下面的表达式中,进行了左移赋值操作。

int x = 7;
x <<= 32;
printf("x = %d\n",x);

上述两个表达式给出了不同的结果。但是下面两个表达式就不一样了。他们都给出了相同的结果。那么上述表达式返回不同值的原因可能是什么?

int a;
a = 1 + 1;
printf("a = %d\n",a);

int b = 1;
b += 1;
printf("b = %d\n",b);
4

3 回答 3

26

C标准说:

如果右操作数为负数,或者大于或等于左表达式类型中的位数,则结果未定义。

所以,这是未定义的行为,因为int通常是32位大小,这意味着只有0通过31步骤是明确定义的

于 2017-05-30T06:47:55.883 回答
5

我同意Cody Gray 的评论。只是对于那些最终来到这里的人来说,解决这种歧义的方法是使用 unsigned long long。

unsigned long long int b = 7ULL<<32; // ULL here is important, as it tells the compiler that the number being shifted is more than 32bit.

unsigned long long int a = 7;
a <<=32;
于 2017-05-30T09:24:05.303 回答
1

来自的抽象操作语义ISO/IEC 9899说:

6.5.7 Bitwise shift operators --- Semantics

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

在您的情况下,拆卸并查看会发生什么,我们会看到:

[root@arch stub]# objdump -d a.out | sed '/ <main>/,/^$/ !d'
00000000004004f6 <main>:
  4004f6:       55                      push   %rbp
  4004f7:       48 89 e5                mov    %rsp,%rbp
  4004fa:       48 83 ec 10             sub    $0x10,%rsp
  4004fe:       c7 45 fc 07 00 00 00    movl   $0x7,-0x4(%rbp)
  400505:       b8 20 00 00 00          mov    $0x20,%eax
  40050a:       89 c1                   mov    %eax,%ecx
  40050c:       d3 65 fc                shll   %cl,-0x4(%rbp)  <<== HERE IS THE PROBLEM
  40050f:       8b 45 fc                mov    -0x4(%rbp),%eax
  400512:       89 c6                   mov    %eax,%esi
  400514:       bf b4 05 40 00          mov    $0x4005b4,%edi
  400519:       b8 00 00 00 00          mov    $0x0,%eax
  40051e:       e8 cd fe ff ff          callq  4003f0 <printf@plt>
  400523:       b8 00 00 00 00          mov    $0x0,%eax
  400528:       c9                      leaveq 
  400529:       c3                      retq   
  40052a:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

生成的代码确实尝试移动,但shll %cl,-0x4(%rbp)(长的左移)没有效果。

undefined behaviour这种情况下,在于组装,即在 SHL 操作中。

于 2017-05-30T07:05:44.943 回答