Pat*_*Pat 5 c standards bit-shift
这个问题首先受到此代码的(意外)结果的启发:
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
Run Code Online (Sandbox Code Playgroud)
但事实证明这是有道理的:
6.5.7按位移位运算符
约束
2每个操作数应具有整数类型
因此,最初混淆的线相当于:
t16 = (uint16_t) (((int) t8) << 1);
Run Code Online (Sandbox Code Playgroud)
有点不直观的恕我直言,但至少定义明确.
好的,很棒,但接下来我们做了:
{
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
}
Run Code Online (Sandbox Code Playgroud)
// edit:遵循与上面相同的文字参数,以下内容应该是等效的:
t64 = (uint64_t) (((int) t64) << 31);
Run Code Online (Sandbox Code Playgroud)
//因此我的困惑/期望[end_edit]
现在,我们得到了直观的结果,但不是从我(标准)阅读标准得到的结果.何时/如何进行"进一步自动型推广"?或者在其他地方是否存在限制类型永远不会被降级(这有意义?),在这种情况下,促销规则如何适用于:
uint32_t << uint64_t
Run Code Online (Sandbox Code Playgroud)
由于标准确实说两个参数都被提升为int; 这两个论点都应该提升到同一类型吗?
//编辑:
更具体地说,结果应该是什么:
uint32_t t32 = 1;
uint64_t t64_one = 1;
uint64_t t64_res;
t64_res = t32 << t64_one;
Run Code Online (Sandbox Code Playgroud)
//结束编辑
当我们认识到规范不要求促销int具体而非要求integer typeuint64_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
Run Code Online (Sandbox Code Playgroud)
如果uint8_t是整数类型,为什么会提升(t8 << t8_one)表达式?
-
作为参考,我的工作是2005年5月6日的ISO/IEC 9899:TC9,WG14/N1124.如果这已经过时,有人也可以提供更新版本的链接,那也值得赞赏.
§6.5.7中的约束"每个操作数应具有整数类型".是一个约束,意味着您不能在非整数类型(如浮点值或指针)上使用按位移位运算符.它不会导致您注意到的效果.
确实导致效果的部分在下一段中:
3.对每个操作数执行整数提升.结果的类型是提升的左操作数的类型.
的整数优惠在§6.3.1.1中描述:
2.以下内容可用于任何地方
int或unsigned int可能使用的表达方式:
- 一个整数类型,其整数转换等级的对象或表达是小于或等于的秩
int和unsigned int.- 类型的位字段
_Bool,int,signed int,或unsigned int.如果a
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;
Run Code Online (Sandbox Code Playgroud)
将2存储的值存储起来t64_res.不过,请注意,这不是受事实表达式的结果是不uint64_t-这和例如表达的会受到影响的是:
uint32_t t32 = 0xFF000;
uint64_t t64_shift = 16;
uint64_t t64_res;
t64_res = t32 << t64_shift;
Run Code Online (Sandbox Code Playgroud)
这里的结果是0xf0000000.
请注意,尽管细节相当复杂,但您可以将其全部归结为一个相当简单的规则,您应该记住:
在C中,算术永远不会在比
int/ 更窄的类型中完成unsigned int.
| 归档时间: |
|
| 查看次数: |
2600 次 |
| 最近记录: |