Sil*_*nic 6 c++ signed bit-manipulation language-lawyer
我正在阅读C++ Primer,我对一些关于Bitwise运算符如何处理有符号类型的注释感到有些困惑.我会引用:
引用#1
(当谈到按位运算符时)"如果操作数是有符号的并且其值是负的,那么在许多按位运算中处理"符号位"的方式取决于机器.此外,执行左移位会改变符号位的值未定义"
引用#2
(当谈到右移操作符时)"如果该操作数是无符号的,则操作符在左侧插入0值位;如果是有符号类型,则结果是实现定义 - 符号位的副本或0值位插在左边."
按位运算符将小整数(例如char)提升为已签名的整数.当按位运算符经常在已签名的运算符类型上给出未定义或实现定义的行为时,此促销对于签名整数是否存在问题?为什么标准不会将char提升为unsigned int?
编辑:这是我提出的问题,但我已将其放回上下文,并在下面给出了一些答案.
之后的一个练习问道
" ~'q' << 6在具有32位ints和8位chars的机器上,使用其中'q'具有位模式的Latin-1字符集的价值是01110001多少?"
好吧,'q'是一个字符文字,将被提升为int,给予
~'q' == ~0000000 00000000 00000000 01110001 == 11111111 11111111 11111111 10001110
下一步骤是将左移位运算适用于上面的位,但作为报价#1提到
"做一个改变符号位值的左移是未定义的"
好吧,我不知道哪个位是符号位,但肯定答案是未定义的?
你是完全正确的 - ~'q' << 6根据标准,表达式是未定义的行为.它甚至比你所说的更糟糕,因为~运算符被定义为计算值的"一个补码",对于有符号(2s补码)整数没有意义 - 术语"一个补码"对于无符号实际上只是意味着什么整数.
在进行按位运算时,如果要严格定义(根据标准)结果,通常必须确保操作的值是无符号的.您可以使用显式强制转换或U在二进制操作中使用显式无符号常量(-suffix)来执行此操作.使用signed和unsigned int执行二进制操作是无符号的(有符号值转换为unsigned).
C和C++与整数提升有微妙的不同,所以你需要小心 - 在与其他操作数比较之前,C++会将小于int的无符号值转换为int(signed),以查看应该做什么,而C将首先比较操作数.
阅读标准的确切文本,而不是像Primer Plus中的摘要,可能最简单。(由于必须是摘要,因此摘要必须省略细节!)
相关部分是:
[expr.shift]
移位运算符
<<和>>组从左到右。操作数应为整数或无范围枚举类型,并执行整数提升。结果的类型是提升后的左操作数的类型。如果右操作数为负或大于或等于提升后的左操作数的位长度,则该行为是不确定的。的值
E1 << E2是E1左移位的E2位置;空出的位为零。如果E1具有无符号类型,则结果的值为E1×2E2,比结果类型中可表示的最大值多模减少1。否则,如果E1具有带符号的类型和非负值,并且E1×2E2在结果类型的相应无符号类型中可表示,则转换为结果类型的那个值就是结果值;否则,行为是不确定的。[expr.unary.op] / 10
的操作数
˜应具有整数或无范围的枚举类型;结果是一个操作数的补码。进行整体促销。结果的类型是提升操作数的类型。
请注意,它们都不执行常规的算术转换(这是大多数二进制运算符完成的对常见类型的转换)。
整体促销:
[conv.prom] / 1
以外的整数类型的prvalue
bool,char16_t,char32_t,或wchar_t,其整数转换秩小于INT的秩可以被转换成类型的prvalueint如果int可以表示源类型的所有值; 否则,可以将源prvalue转换为type的prvalueunsigned int。
(“其他”列表中还包含其他类型的条目,此处已将其省略,但您可以在标准草稿中查找)。
关于整数促销,要记住的是它们是保值的,如果您具有char价值-30,那么在促销之后,它将是一种int价值-30。您无需考虑“符号扩展”之类的事情。
您对的初步分析~'q'是正确的,并且结果具有类型int(因为int可以表示char正常系统上的所有值)。
事实证明,int设置了最高有效位的任何对象都表示一个负值(在标准的另一部分中有关于此的规则,我在这里没有引用),所以~'q'是负数int。
查看[expr.shift] / 2,我们看到这意味着向左移动会导致未定义的行为(该段中的任何较早的情况都未涵盖)。
| 归档时间: |
|
| 查看次数: |
2528 次 |
| 最近记录: |