这个问题首先受到此代码的(意外)结果的启发:
uint16_t t16 = 0;
uint8_t t8 = 0x80;
uint8_t t8_res;
t16 = (t8 << 1);
t8_res = (t8 << 1);
printf("t16: %x\n", t16); // Expect 0, get 0x100
printf(" t8: %x\n", t8_res); // Expect 0, get 0
但事实证明这是有道理的:
6.5.7按位移位运算符
约束
2 每个操作数都应具有整数类型
因此最初混淆的行相当于:
t16 = (uint16_t) (((int) t8) << 1);
有点不直观的恕我直言,但至少定义明确。
好的,很好,但接下来我们做了:
{
uint64_t t64 = 1;
t64 <<= 31;
printf("t64: %lx\n", t64); // Expect 0x80000000, get 0x80000000
t64 <<= 31;
printf("t64: %lx\n", t64); // Expect 0x0, get 0x4000000000000000
}
// edit:遵循与上面相同的文字参数,以下内容应该是等效的:
t64 = (uint64_t) (((int) t64) << 31);
//因此我的困惑/期望[end_edit]
现在,我们得到了直观的结果,但不是从我(标准)阅读标准得到的结果。何时/如何进行“进一步自动型推广”?或者在其他地方是否存在限制类型永远不会降级(这有意义?),在这种情况下,促销规则如何适用于:
uint32_t << uint64_t
由于标准确实说两个参数都被提升为int;两个论点都应该在这里被提升为相同的类型吗?
//编辑:
更具体地说,结果应该是什么:
uint32_t t32 = 1;
uint64_t t64_one = 1;
uint64_t t64_res;
t64_res = t32 << t64_one;
//结束编辑
当我们认识到规范并不要求向int
专门宣传,而是要求integer type
,uint64_t符合条件时,上述问题的答案就解决了。
//澄清编辑:
好的,但现在我又困惑了。具体来说,如果uint8_t
是整数类型,那为什么它会被提升为int
?它似乎与常量int 1无关,如下面的练习所示:
{
uint16_t t16 = 0;
uint8_t t8 = 0x80;
uint8_t t8_one = 1;
uint8_t t8_res;
t16 = (t8 << t8_one);
t8_res = (t8 << t8_one);
printf("t16: %x\n", t16);
printf(" t8: %x\n", t8_res);
}
t16: 100
t8: 0
如果uint8_t是整数类型,为什么会提升(t8&lt;&lt;&lt;&lt; t8_one)表达式?
-
作为参考,我正在使用ISO / IEC 9899:TC9,WG14 / N1124 2005年5月6日。如果这已经过时,有人也可以提供更新版本的链接,那么我们将不胜感激。好。
答案 0 :(得分:7)
我认为您混淆的原因可能是以下两个陈述不等于:
int
类型 uint64_t
是整数类型。
答案 1 :(得分:5)
§6.5.7中的约束“每个操作数应具有整数类型。”是一个约束,意味着你不能在非整数类型上使用按位移位运算符,如浮点值或指针。它不会引起你注意到的效果。
导致效果的部分在下一段中:
3.对每个操作数执行整数提升。结果的类型是提升的左操作数的类型。
整数促销在§6.3.1.1中描述:
2.在
int
的任何地方都可以使用以下内容 或unsigned int
可以使用:
- 具有整数类型的对象或表达式,其整数转换等级小于或等于
int
的等级和unsigned int
。- 类型为
_Bool
,int
,signed int
或unsigned int
的位字段。如果
int
可以表示原始类型的所有值,则为该值 转换为int
;否则,它将转换为unsigned int
。这些被称为整数促销。所有其他类型 整数促销没有改变。
uint8_t
的排名低于int
,因此该值会转换为int
(因为我们知道int
必须能够代表所有值uint8_t
,考虑到这两种类型的范围要求。)
排名规则很复杂,但它们可以保证排名较高的类型不会有较低的精度。实际上,这意味着整数促销不能将类型“降级”为精度较低的类型(uint64_t
可以提升为int
或unsigned int
,但仅如果类型的范围至少是uint64_t
的范围。
在uint32_t << uint64_t
的情况下,启动的规则是“结果的类型是提升的左操作数的类型”。所以我们有几个可能性:
int
至少为33位,则uint32_t
将提升为int
,结果将为int
; int
小于33位且unsigned int
至少为32位,则uint32_t
将提升为unsigned int
,结果将为unsigned int
}; unsigned int
小于32位,则uint32_t
将保持不变,结果将为uint32_t
。在今天常见的桌面和服务器实现中,int
和unsigned int
通常是32位,因此第二种可能性会发生(uint32_t
被提升为unsigned int
)。在过去,int
/ unsigned int
通常为16位,第三种可能性(uint32_t
未启动)。
您的示例的结果:
uint32_t t32 = 1;
uint64_t t64_one = 1;
uint64_t t64_res;
t64_res = t32 << t64_one;
将值2
存储到t64_res
中。请注意,这不受表达式结果不是uint64_t
的影响 - 并且将受影响的表达式的示例是:
uint32_t t32 = 0xFF000;
uint64_t t64_shift = 16;
uint64_t t64_res;
t64_res = t32 << t64_shift;
此处的结果为0xf0000000
。
请注意,尽管细节相当复杂,但您可以将其全部归结为一个应该记住的相当简单的规则:
在C中,算术永远不会在比
int
/更窄的类型中完成unsigned int
。
答案 2 :(得分:3)
你在标准中找到了错误的规则:(相关的是类似“通常的整数类型促销适用”。这就是第一个例子的命中。如果像uint8_t
这样的整数类型有排名如果小于int
,则会将其提升为int
。uint64_t
的排名不会小于int
或unsigned
,因此不会执行任何促销活动<<
运算符应用于uint64_t
变量。
编辑:所有小于int
的整数类型都会被提升为算术运算。这只是生活中的一个事实:)是否提升uint32_t
取决于平台,因为它可能具有相同等级或高于int
(未提升)或更低等级(提升)
关于<<
运算符,右操作数的类型并不重要,位数的数量是左数(具有上述规则)。对于正确的一个更重要的是它的价值。它不是负数或超过(提升的)左操作数的宽度。