如果在一个表达式中不同时使用左移和右移,为什么会有所不同? [英] Why does it make a difference if left and right shift are used together in one expression or not?
问题描述
我有以下代码:
unsigned char x = 255;
printf("%x\n", x); // ff
unsigned char tmp = x << 7;
unsigned char y = tmp >> 7;
printf("%x\n", y); // 1
unsigned char z = (x << 7) >> 7;
printf("%x\n", z); // ff
我希望y
和z
相同.但是,根据是否使用中间变量,它们会有所不同.知道为什么会这样很有趣.
I would have expected y
and z
to be the same. But they differ depending on whether a intermediary variable is used. It would be interesting to know why this is the case.
推荐答案
这个小测试实际上比它看起来更微妙,因为行为是由实现定义的:
This little test is actually more subtle than it looks as the behavior is implementation defined:
-
unsigned char x = 255;
在这里没有歧义,x
是值为255
的unsigned char
,保证类型unsigned char
具有足够的范围来存储255
.
unsigned char x = 255;
no ambiguity here,x
is anunsigned char
with value255
, typeunsigned char
is guaranteed to have enough range to store255
.
printf("%x\n", x);
这会在标准输出上生成ff
,但是写printf("%hhx\n", x);
会更干净,因为printf
期望将unsigned int
用于转换%x
,而x
不是.传递x
实际上可能会传递int
或unsigned int
参数.
printf("%x\n", x);
This produces ff
on standard output but it would be cleaner to write printf("%hhx\n", x);
as printf
expects an unsigned int
for conversion %x
, which x
is not. Passing x
might actually pass an int
or an unsigned int
argument.
unsigned char tmp = x << 7;
为了评估表达式x << 7
,作为unsigned char
的x
首先要接受C标准 6.3.3.1 中定义的整型促销. strong>:如果int
可以表示原始类型的所有值(受位字段的宽度限制),则该值将转换为int
;否则,该值将转换为int
.否则,它将转换为unsigned int
.这些称为整数促销.
unsigned char tmp = x << 7;
To evaluate the expression x << 7
, x
being an unsigned char
first undergoes the integer promotions defined in the C Standard 6.3.3.1: If an int
can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int
; otherwise, it is converted to an unsigned int
. These are called the integer promotions.
因此,如果unsigned char
中的值位数小于或等于int
(当前最常见的情况是8 vs 31),则首先将x
提升为具有相同值的int
值,然后向左移动7
个位置.结果0x7f80
确保适合int
类型,因此行为已得到很好的定义,并将此值转换为类型unsigned char
将有效地截断该值的高阶位.如果类型unsigned char
具有8位,则值将为128
(0x80
),但是如果类型unsigned char
具有更多位,则tmp
中的值可以为0x180
,0x380
,0xf80
,0x1f80
,0x3f80
甚至是0x7f80
.
So if the number of value bits in unsigned char
is smaller or equal to that of int
(the most common case currently being 8 vs 31), x
is first promoted to an int
with the same value, which is then shifted left by 7
positions. The result, 0x7f80
, is guaranteed to fit in the int
type, so the behavior is well defined and converting this value to type unsigned char
will effectively truncate the high order bits of the value. If type unsigned char
has 8 bits, the value will be 128
(0x80
), but if type unsigned char
has more bits, the value in tmp
can be 0x180
, 0x380
, 0x780
, 0xf80
, 0x1f80
, 0x3f80
or even 0x7f80
.
如果类型unsigned char
大于int
,这可能发生在少数系统中,其中sizeof(int) == 1
,x
被提升为unsigned int
,并且对该类型执行左移.值是0x7f80U
,可以保证适合unsigned int
类型,并且将其存储到tmp
实际上不会丢失任何信息,因为类型unsigned char
具有与unsigned int
相同的大小.因此,在这种情况下,tmp
的值将为0x7f80
.
If type unsigned char
is larger than int
, which can occur on rare systems where sizeof(int) == 1
, x
is promoted to unsigned int
and the left shift is performed on this type. The value is 0x7f80U
, which is guaranteed to fit in type unsigned int
and storing that to tmp
does not actually lose any information since type unsigned char
has the same size as unsigned int
. So tmp
would have the value 0x7f80
in this case.
unsigned char y = tmp >> 7;
评估与上述步骤相同,根据系统将tmp
提升为int
或unsigned int
,并保留其值,并将该值右移7个位置,由于7
小于类型的宽度(int
或unsigned int
)并且值是正数,因此已完全定义.根据unsigned char
类型的位数,存储在y
中的值可以是1
,3
,7
,15
,31
,63
,127
或255
,最常见的体系结构将是y == 1
.
unsigned char y = tmp >> 7;
The evaluation proceeds the same as above, tmp
is promoted to int
or unsigned int
depending on the system, which preserves its value, and this value is shifted right by 7 positions, which is fully defined because 7
is less than the width of the type (int
or unsigned int
) and the value is positive. Depending on the number of bits of type unsigned char
, the value stored in y
can be 1
, 3
, 7
, 15
, 31
, 63
, 127
or 255
, the most common architecture will have y == 1
.
printf("%x\n", y);
,最好写printf("%hhx\n", y);
,输出可能是1
(最常见的情况)或3
,7
,f
,1f
, 3f
,7f
或ff
,具体取决于unsigned char
类型的值位数.
printf("%x\n", y);
again, it would be better t write printf("%hhx\n", y);
and the output may be 1
(most common case) or 3
, 7
, f
, 1f
, 3f
, 7f
or ff
depending on the number of value bits in type unsigned char
.
unsigned char z = (x << 7) >> 7;
如上所述,在x
上执行整数提升,然后将值(255
)左移7位,作为int
或unsigned int
,始终产生0xff
.此行为已完全定义.
unsigned char z = (x << 7) >> 7;
The integer promotion is performed on x
as described above, the value (255
) is then shifted left 7 bits as an int
or an unsigned int
, always producing 0x7f80
and then right shifted by 7 positions, with a final value of 0xff
. This behavior is fully defined.
printf("%x\n", z);
再一次,格式字符串应为printf("%hhx\n", z);
,输出将始终为ff
.
printf("%x\n", z);
Once more, the format string should be printf("%hhx\n", z);
and the output would always be ff
.
如今,字节多于8位的系统变得越来越稀少,但是某些嵌入式处理器(例如专用DSP)仍然可以做到这一点.为%x
转换说明符传递unsigned char
时,将导致一个错误的系统失败,但是使用%hhx
或更方便地编写printf("%x\n", (unsigned)z);
Systems where bytes have more than 8 bits are becoming rare these days, but some embedded processors, such as specialized DSPs still do that. It would take a perverse system to fail when passed an unsigned char
for a %x
conversion specifier, but it is cleaner to either use %hhx
or more portably write printf("%x\n", (unsigned)z);
在此示例中,将移动8
而不是7
会更加人为设计.在具有16位int
和8位char
的系统上,它将具有未定义的行为.
Shifting by 8
instead of 7
in this example would be even more contrived. It would have undefined behavior on systems with 16-bit int
and 8-bit char
.
这篇关于如果在一个表达式中不同时使用左移和右移,为什么会有所不同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!