c标准和移位 [英] c standard and bitshifts
问题描述
此问题最初是受以下代码的(意外)结果启发:
This question was first inspired by the (unexpected) results of this code:
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
但事实证明这是有道理的:
But it turns out this makes sense:
6.5.7按位移位运算符
约束
2 每个操作数应具有整数类型
2 Each of the operands shall have integer type
因此,最初混乱的行等同于:
Thus the originally confused line is equivalent to:
t16 = (uint16_t) (((int) t8) << 1);
有点不直观的恕我直言,但至少定义明确.
A little non-intuitive IMHO, but at least well-defined.
好的,很好,但是我们做到了:
Ok, great, but then we do:
{
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: following the same literal argument as above, the following should be equivalent:
t64 = (uint64_t) (((int) t64) << 31);
//因此,我的困惑/期望[end_edit]
// hence my confusion / expectation [end_edit]
现在,我们得到了直观的结果,但不是从我(标准)阅读的标准中得出的结果.此进一步的自动类型升级"何时/如何进行?还是在其他地方存在某种限制,那就是永远不能降级一种类型(这有意义吗?),在这种情况下,促销规则如何适用:
Now, we get the intuitive result, but not what would be derived from my (literal) reading of the standard. When / how does this "further automatic type promotion" take place? Or is there a limitation elsewhere that a type can never be demoted (that would make sense?), in that case, how do the promotion rules apply for:
uint32_t << uint64_t
由于该标准确实说两个参数都被提升为int;应该在这里将两个参数提升为相同的类型吗?
Since the standard does say both arguments are promoted to int; should both arguments be promoted to the same type here?
//
更具体地说,结果应该是什么:
More specifically, what should the result of:
uint32_t t32 = 1;
uint64_t t64_one = 1;
uint64_t t64_res;
t64_res = t32 << t64_one;
//结束编辑
当我们认识到规范并不需要提升为int
而不是提升为uint64_t合格的integer type
时,就可以解决上述问题.
The answer to the above question is resolved when we recognize that the spec does not demand a promotion to int
specifically, rather to an integer type
, which uint64_t qualifies as.
//澄清
好的,但是现在我又感到困惑了.具体来说,如果uint8_t
是整数类型,那么为什么将其完全提升为int
?似乎与常量int 1无关,如以下练习所示:
Ok, but now I am confused again. Specifically, if uint8_t
is an integer type, then why is it being promoted to int
at all? It does not seem to be related to the constant int 1, as the following exercise demonstrates:
{
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<< t8_one)表达式?
Why is the (t8 << t8_one) expression being promoted if uint8_t is an integer type?
-
作为参考,我使用的是ISO/IEC 9899:TC9,WG14/N1124,2005年5月6日.如果该版本已过时,并且有人还可以提供指向较新版本的链接,我们将不胜感激.好吧.
For reference, I'm working from ISO/IEC 9899:TC9, WG14/N1124 May 6, 2005. If that's out of date and someone could also provide a link to a more recent copy, that'd be appreciated as well.
推荐答案
第6.5.7节中的约束每个操作数应具有整数类型." 是一个约束,表示您不能在非整数类型(例如浮点值或指针)上使用按位移位运算符.它不会引起您所注意的效果.
The constraint in §6.5.7 that "Each of the operands shall have integer type." is a constraint that means you cannot use the bitwise shift operators on non-integer types like floating point values or pointers. It does not cause the effect you are noting.
确实起作用的部分在下一段中:
3.对每个操作数执行整数提升.结果的类型是提升后的左操作数的类型.
3. The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand.
整数促销在第6.3.1.1节中进行了描述:
The integer promotions are described in §6.3.1.1:
2.在
int
所在的表达式中可以使用以下内容 或使用unsigned int
:
2. The following may be used in an expression wherever an
int
orunsigned int
may be used:
- 具有整数类型的对象或表达式,其整数转换等级小于或等于
int
的等级,并且unsigned int
. -
_Bool
,int
,signed int
或unsigned int
类型的位字段.
- An object or expression with an integer type whose integer conversion rank is less than or equal to the rank of
int
andunsigned int
. - A bit-field of type
_Bool
,int
,signed int
, orunsigned int
.
如果int
可以表示原始类型的所有值,则该值
转换为int
;否则,将其转换为unsigned
int
.这些称为整数促销.所有其他类型是
不受整数促销的影响.
If an int
can represent all values of the original type, the value
is converted to an int
; otherwise, it is converted to an unsigned
int
. These are called the integer promotions. All other types are
unchanged by the integer promotions.
uint8_t
的等级比int
小,因此该值将转换为int
(因为我们知道int
必须能够表示uint8_t
的所有值)在这两种类型的范围内.
uint8_t
has a lesser rank than int
, so the value is converted to an int
(since we know that an int
must be able to represent all the values of uint8_t
, given the requirements on the ranges of those two types).
排名规则很复杂,但是它们可以保证排名较高的类型的精度不会较低.实际上,这意味着不能通过整数提升将类型降级"为精度较低的类型(uint64_t
可以提升为int
或unsigned int
,但只能提升为 (如果类型的范围至少是uint64_t
的范围).
The ranking rules are complex, but they guarantee that a type with a higher rank cannot have a lesser precision. This means, in effect, that types cannot be "demoted" to a type with lesser precision by the integer promotions (it is possible for uint64_t
to be promoted to int
or unsigned int
, but only if the range of the type is at least that of uint64_t
).
对于uint32_t << uint64_t
,插入的规则为结果的类型为提升后的左操作数的类型" .因此,我们有几种可能性:
In the case of uint32_t << uint64_t
, the rule that kicks in is "The type of the result is that of the promoted left operand". So we have a few possibilities:
- 如果
int
至少为33位,则uint32_t
将提升为int
,结果将为int
; - 如果
int
小于33位且unsigned int
至少32位,则uint32_t
将提升为unsigned int
,结果将为unsigned int
; - 如果
unsigned int
小于32位,则uint32_t
将保持不变,结果将为uint32_t
.
- If
int
is at least 33 bits, thenuint32_t
will be promoted toint
and the result will beint
; - If
int
is less than 33 bits andunsigned int
is at least 32 bits, thenuint32_t
will be promoted tounsigned int
and the result will beunsigned int
; - If
unsigned int
is less than 32 bits thenuint32_t
will be unchanged and the result will beuint32_t
.
在当今常见的台式机和服务器实现中,int
和unsigned int
通常为32位,因此第二种可能性将发生(uint32_t
升级为unsigned int
).过去,int
/unsigned int
通常为16位,并且第三种可能性会发生(uint32_t
不提倡).
On today's common desktop and server implementations, int
and unsigned int
are usually 32 bits, and so the second possibility will occur (uint32_t
is promoted to unsigned int
). In the past it was common for int
/ unsigned int
to be 16 bits, and the third possibility would occur (uint32_t
left unpromoted).
示例结果:
uint32_t t32 = 1;
uint64_t t64_one = 1;
uint64_t t64_res;
t64_res = t32 << t64_one;
将是存储在t64_res
中的值2
.请注意,尽管这不受表达式结果不是uint64_t
的事实的影响-并且将会受到影响的表达式的示例是:
Will be the value 2
stored into t64_res
. Note though that this is not affected by the fact that the result of the expression is not uint64_t
- and example of an expression that would be affected is:
uint32_t t32 = 0xFF000;
uint64_t t64_shift = 16;
uint64_t t64_res;
t64_res = t32 << t64_shift;
这里的结果是0xf0000000
.
请注意,尽管细节相当复杂,但是您可以将其简化为一个应牢记的相当简单的规则:
Note that although the details are fairly intricate, you can boil it all down to a fairly simple rule that you should keep in mind:
在C语言中,永远不会对小于
int
/的类型进行算术运算unsigned int
.
In C, arithmetic is never done in types narrower than
int
/unsigned int
.
这篇关于c标准和移位的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!