c标准和位移

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.如果这已经过时,有人也可以提供更新版本的链接,那也值得赞赏.

Gre*_*ill 7

我认为你混淆的根源可能是以下两个陈述相同:

  • 每个操作数应具有整数类型
  • 每个操作数都应具有int类型

uint64_t 是整数类型.

  • 实际上,这是OP混乱的根源. (2认同)

caf*_*caf 5

§6.5.7中的约束"每个操作数应具有整数类型".是一个约束,意味着您不能在非整数类型(如浮点值或指针)上使用按位移位运算符.它不会导致您注意到的效果.

确实导致效果的部分在下一段中:

 3.对每个操作数执行整数提升.结果的类型是提升的左操作数的类型.

整数优惠在§6.3.1.1中描述:

 2.以下内容可用于任何地方intunsigned int可能使用的表达方式:

  • 一个整数类型,其整数转换等级的对象或表达是小于或等于的秩intunsigned 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.