c标准和位移

时间:2011-10-30 21:32:41

标签: 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

但事实证明这是有道理的:

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日。如果这已经过时,有人也可以提供更新版本的链接,那么我们将不胜感激。好。

3 个答案:

答案 0 :(得分:7)

我认为您混淆的原因可能是以下两个陈述不等于

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

uint64_t是整数类型。

答案 1 :(得分:5)

§6.5.7中的约束“每个操作数应具有整数类型。”是一个约束,意味着你不能在非整数类型上使用按位移位运算符,如浮点值或指针。它不会引起你注意到的效果。

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

  

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

整数促销在§6.3.1.1中描述:

  

2.在int的任何地方都可以使用以下内容   或unsigned int可以使用:

     
      
  • 具有整数类型的对象或表达式,其整数转换等级小于或等于int的等级和   unsigned int
  •   
  • 类型为_Boolintsigned intunsigned int的位字段。
  •   
     

如果int可以表示原始类型的所有值,则为该值   转换为int;否则,它将转换为unsigned int。这些被称为整数促销。所有其他类型   整数促销没有改变。

uint8_t的排名低于int,因此该值会转换为int(因为我们知道int必须能够代表所有值uint8_t,考虑到这两种类型的范围要求。)

排名规则很复杂,但它们可以保证排名较高的类型不会有较低的精度。实际上,这意味着整数促销不能将类型“降级”为精度较低的类型(uint64_t可以提升为intunsigned 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

在今天常见的桌面和服务器实现中,intunsigned 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,则会将其提升为intuint64_t的排名不会小于intunsigned,因此不会执行任何促销活动<<运算符应用于uint64_t变量。

编辑:所有小于int的整数类型都会被提升为算术运算。这只是生活中的一个事实:)是否提升uint32_t取决于平台,因为它可能具有相同等级或高于int(未提升)或更低等级(提升)

关于<<运算符,右操作数的类型并不重要,位数的数量是左数(具有上述规则)。对于正确的一个更重要的是它的价值。它不是负数或超过(提升的)左操作数的宽度。