直接将左移运算的结果赋值给一个变量和C中的左移赋值运算有什么区别?
What is the difference between directly assigning the result of left shift operation to a variable and the left shift assignment operation in C?
在下面的表达式中,将左移运算的结果赋值给变量i
。
int i;
i = 7 << 32;
printf("i = %d\n",i);
在下面的表达式中,进行了左移赋值操作
int x = 7;
x <<= 32;
printf("x = %d\n",x);
以上两个表达式给出了不同的结果。但是下面两个表达式就不一样了。他们都给出了相同的结果。那么上述表达式 return 不同值的原因可能是什么?
int a;
a = 1 + 1;
printf("a = %d\n",a);
int b = 1;
b += 1;
printf("b = %d\n",b);
C 标准说:
The result is undefined if the right operand is negative, or greater
than or equal to the number of bits in the left expression’s type.
所以,这是 未定义的行为 因为 int
通常是 32
位大小,这意味着只有 0
到 31
步骤是 明确定义的 。
来自 ISO/IEC 9899
的抽象操作语义说:
6.5.7 Bitwise shift operators --- Semantics
3 .... ... . 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.
在你的情况下,拆开看看会发生什么,我们是这样看的:
[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 [=11=]x10,%rsp
4004fe: c7 45 fc 07 00 00 00 movl [=11=]x7,-0x4(%rbp)
400505: b8 20 00 00 00 mov [=11=]x20,%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 [=11=]x4005b4,%edi
400519: b8 00 00 00 00 mov [=11=]x0,%eax
40051e: e8 cd fe ff ff callq 4003f0 <printf@plt>
400523: b8 00 00 00 00 mov [=11=]x0,%eax
400528: c9 leaveq
400529: c3 retq
40052a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
生成的代码确实尝试移位,但 shll %cl,-0x4(%rbp)
(long 的左移)没有效果。
本例中的undefined behaviour
在于汇编,即在SHL操作中。
我同意 。只是为了未来的人最终来到这里,解决这种歧义的方法是使用 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;
在下面的表达式中,将左移运算的结果赋值给变量i
。
int i;
i = 7 << 32;
printf("i = %d\n",i);
在下面的表达式中,进行了左移赋值操作
int x = 7;
x <<= 32;
printf("x = %d\n",x);
以上两个表达式给出了不同的结果。但是下面两个表达式就不一样了。他们都给出了相同的结果。那么上述表达式 return 不同值的原因可能是什么?
int a;
a = 1 + 1;
printf("a = %d\n",a);
int b = 1;
b += 1;
printf("b = %d\n",b);
C 标准说:
The result is undefined if the right operand is negative, or greater than or equal to the number of bits in the left expression’s type.
所以,这是 未定义的行为 因为 int
通常是 32
位大小,这意味着只有 0
到 31
步骤是 明确定义的 。
来自 ISO/IEC 9899
的抽象操作语义说:
6.5.7 Bitwise shift operators --- Semantics
3 .... ... . 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.
在你的情况下,拆开看看会发生什么,我们是这样看的:
[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 [=11=]x10,%rsp
4004fe: c7 45 fc 07 00 00 00 movl [=11=]x7,-0x4(%rbp)
400505: b8 20 00 00 00 mov [=11=]x20,%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 [=11=]x4005b4,%edi
400519: b8 00 00 00 00 mov [=11=]x0,%eax
40051e: e8 cd fe ff ff callq 4003f0 <printf@plt>
400523: b8 00 00 00 00 mov [=11=]x0,%eax
400528: c9 leaveq
400529: c3 retq
40052a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
生成的代码确实尝试移位,但 shll %cl,-0x4(%rbp)
(long 的左移)没有效果。
本例中的undefined behaviour
在于汇编,即在SHL操作中。
我同意
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;