Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/71.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
直接将左移操作的结果赋值给变量与C中的左移赋值操作有什么区别?_C_Bit Shift_Relational Operators - Fatal编程技术网

直接将左移操作的结果赋值给变量与C中的左移赋值操作有什么区别?

直接将左移操作的结果赋值给变量与C中的左移赋值操作有什么区别?,c,bit-shift,relational-operators,C,Bit Shift,Relational Operators,在以下表达式中,左移位操作的结果被分配给变量i int i; i = 7 << 32; printf("i = %d\n",i); C标准说: 如果右操作数为负或更大,则结果为未定义 大于或等于左表达式类型中的位数 因此,它是未定义的行为,因为int的大小通常为32位,这意味着只有0到31步骤是定义良好的ISO/IEC 9899中的抽象操作语义表示: 6.5.7 Bitwise shift operators --- Semantics 3。如果值 右操作数的值为负或大于或等于

在以下表达式中,左移位操作的结果被分配给变量
i

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

C标准说:

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


因此,它是未定义的行为,因为
int
的大小通常为
32
位,这意味着只有
0
31
步骤是定义良好的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)
[root@arch存根]#objdump-d a.out | sed'//,/^$/!d'
0000000000 4004F6:
4004f6:55推送%rbp
4004f7:48 89 e5 mov%rsp,%rbp
4004fa:48 83 ec 10分$0x10,%rsp
4004fe:c7 45 fc 07 00 movl$0x7,-0x4(%rbp)
400505:b8 20 00 mov$0x20,%eax
40050a:89 c1 mov%eax,%ecx
40050c:D365 fc shll%cl,-0x4(%rbp)我同意。对于将来来到这里的人来说,解决这种歧义的方法是使用无符号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;


unsigned long long int b=7ull编译器对
int
数据类型使用了多少位?请尝试按2位而不是32位移位,然后查看结果!提示:在启用所有警告的情况下编译。这些代码块不是表达式。它们是一组语句。重复:31个步骤也是未定义的,除非输入为零(将1移到符号位)“因为
int
的大小通常为32位”:这取决于编译器。@M.M否,1在左侧的衰减是可以的。实际上,它只是移位超过(或等于)基础类型中未定义的位数,而不管这些位数的实际值是多少。@M.M ISO99 6.5.7:表示它是为无符号类型定义的。它确实声明它对于有符号整数是未定义的,但依我看,这只是有符号溢出规则的一个实例,不特定于左移位。@MarcSchütz它仍然适用,无论您想如何对它进行分类-假设32位int,
1“在这种情况下,未定义的行为在于汇编,即在SHL操作中。”这是不正确的。在x86上定义了过多的位移位,如中所示。如果您在汇编中编写代码,就不会有问题。它是未定义行为的原因是C语言标准中规定的要求。“…shll%cl,-0x4(%rbp)
(长左移)无效。”我不知道你为什么这么说。从技术上讲,
shll%cl,-0x4(%rbp)
确实会修改堆栈上的值。为什么不呢?指令要求将
rbp
的偏移量-4处的值按
cl
中的值进行移位,这正是它所做的。现在,x86 ISA被专门记录为将32位操作数的移位计数屏蔽为5位,这(A)是这种定义良好的行为的原因,(B)意味着将值移位32基本上是一种标识操作,因为它会计算原始值。因此,从这个意义上讲,不,
-0x4(%rbp)
处的值没有修改,但它不应该被修改。我很难理解您的观点。从文档中可以非常清楚地看到,在x86上移位过多的位并不是未定义的行为;事实上,它的定义非常明确,因此我们可以准确地预测7乘32的移位结果。组件中没有“问题”。问题在于编译器正在将C源代码转换为机器代码,而C源代码由于表现出未定义的行为而无效。答案的上半部分是正确的;下半部分是误导性的或完全错误的。换句话说,如果一个人用x86汇编语言手工编写这个程序,我们可能会说它有一个bug,如果这个人的意思是左移去做它不做的事情,但我们不会说它有未定义的行为,因为所有的机器指令都有定义良好的行为。(我们可能不喜欢Intel指定
shll
来减少mod 32的移位计数,但他们确实指定了它。)然而,被翻译成这个程序集转储的C程序确实有未定义的行为,因为C标准没有指定过宽移位计数的作用。@dan我想你误解了我的观点,我担心阿林索尔是。我完全同意这在C语言中是未定义的行为,但这并不会因为一个特定的编译器在针对一个特定的体系结构时似乎始终将代码转换为特定的机器指令而减少未定义的行为。我的问题是在回答中提出的“本例中未定义的行为存在于汇编中,即在SHL操作中。”这是错误的,在汇编语言中未定义该行为,它在C中是未定义的。在这种情况下,64位移位的情况是相同的?代码中的任何幻数通常是字长,除非另有类型转换。在这种情况下,当您试图右移32位(即等于字长的大小)时,我们需要告诉编译器不要将其视为一个字宽数据(32位)。在考虑按
n位
移位时,可能需要谨慎,以推荐使用
int64_t
uint64_t
,明确确保位长度的
stdint
中的etc是+1并重申:始终使用stdint类型
[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)
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;