c++ - 小类型(char和short)的来回移位行为
问题描述
假设我想将变量的前i位设置c
为零。一种方法是左移i位,然后右移相同的量。这是一个执行此操作的简单程序:
#include <iostream>
int main() {
using type = unsigned int;
type c, i;
std::cin >> c >> i;
c = (c << i) >> i;
std::cout << c << "\n";
return 0;
}
但是当类型是unsigned short
or时unsigned char
,这不起作用,并且c
保持不变。从一方面来看,这是完全可以预料的,因为我们知道寄存器至少有 32 位宽,并且来回移动一两个字节不会将最左边的位设置为零。但问题是:这样的行为如何符合标准和 operator<< 的定义?c = (c << i) >> i;
从语言的角度来看,行为不一样的原因是什么c <<= i; c >>= i;
?它甚至是定义的行为吗?如果是,是否有其他示例在语义等效的代码之间呈现不同的行为?(或者为什么这两行不等效?)
我还查看了生成的程序集,并且使用 -O2 它看起来更多或对于任何类型,都不像这样:
sall %cl, %esi
shrl %cl, %esi
但是如果我们让i保持不变,那么 g++ 用2^(n_bits - i) - 1屏蔽整数,但是从不费心为短裤和字符生成任何指令,并在从 cin 获取后立即打印它们。因此,它肯定知道它是如何工作的,因此即使我找不到任何东西,也应该在某处记录这种行为。
PS当然有更可靠的方法将所需的位设置为零,例如gcc在知道i时使用的方法,但这个问题更多的是关于行为规则而不是设置位域。
解决方案
这种行为如何符合标准和 operator<<? 的定义?
您观察到的行为符合标准。
它甚至是定义的行为吗
是的,它已定义(假设i
不会太大而导致溢出;您将无法使用此方法将所有位设置为零)。
为什么这两行不等价?
因为没有比int
C++ 低等级的整数类型的算术运算,并且所有较小类型的算术操作数都被隐式转换为有符号 int
的。这种隐式转换称为提升。
有符号右移和无符号右移的行为是不同的。有符号右移扩展最左边的位以使符号保持不变,而无符号右移用零填充最左边的位。
第二个版本的行为不同,因为中间结果具有较小的无符号类型,而第一个版本中的中间结果是提升的有符号类型int
(在short
和char
小于的系统上int
)。
推荐阅读
- loopbackjs - 多用户角色环回
- javascript - 打开带有 angular 6 viewchild 的 bootstrap 3.3.7 模态
- javascript - 减去标题高度后,将图像的高度设置为等于视口的高度
- c# - 如何在水晶报表中添加滚动条?
- android - 如何从项目中删除 AdMob?
- ios - swift:collectionView 单元格中的标签
- python - 初始化整数变量以进行比较
- angular - 如何获得 10KB 以下的 Angular Hello World 应用程序?
- java - 爪哇。GSON。如何将参数传递给 toJson()?
- python - Python 2.7 Rock Paper Scissors 未按预期工作