当我使用shift >>
<<
进行操作时,我最近发现了一种(奇怪的)行为!
为了解释它,让我编写这个小的可运行代码,执行两个应该相同的操作(在我的理解中),但我对不同的结果感到惊讶!
#include <stdio.h>
int main(void) {
unsigned char a=0x05, b=0x05;
// first operation
a = ((a<<7)>>7);
// second operation
b <<= 7;
b >>= 7;
printf("a=%X b=%X\n", a, b);
return 0;
}
运行时,a = 5
和b = 1
。我希望它们都等于1!有人可以解释为什么我得到这样的结果吗?
P.S:在我的环境中,unsigned char
的大小为1个字节
答案 0 :(得分:31)
在第一个例子中:
a
转换为int
,向左移动,然后向右移动,然后转换回usigned char
。 这显然会导致a=5
。
在第二个例子中:
b
转换为int
,向左移动,然后转换回unsigned char
。b
转换为int
,向右移,然后转换回unsigned char
。不同之处在于您在转换为unsigned char
答案 1 :(得分:17)
行之间发生的事情的详细解释:
案例a:
a = ((a<<7)>>7);
中,首先评估a<<7
。int
。 int
。a
被提升为int
类型,仍然包含值0x05。 7
字面值已经是int
类型,因此无法升级。int
向左移动7时,您将获得0x0280。操作的结果是int
类型。int
是一个有符号的类型,所以如果你继续将数据转移到符号位,你就会调用未定义的行为。同样,如果左侧或右侧操作数为负值,您还可以调用未定义的行为。0x280 >> 7;
。由于两个操作数都已经为int,因此下一个移位操作不会进行任何促销。 案例b:
b <<= 7;
相当于b = b << 7;
。b
会升级为int
。结果将再次为0x0280。0x80
。b
再次被提升为包含0x80的int。好建议:
stdint.h
中的类型而不是C中的原始默认类型。编写程序的更好,更安全的方法是:
#include <stdio.h>
#include <stdint.h>
int main(void) {
uint8_t a=0x05;
uint8_t b=0x05;
uint32_t tmp;
// first operation
tmp = (uint32_t)a << 7;
tmp = tmp >> 7;
a = (uint8_t)tmp;
// second operation
tmp = (uint32_t)b << 7;
tmp = tmp >> 7;
b = (uint8_t)tmp;
printf("a=%X b=%X\n", a, b);
return 0;
}
答案 2 :(得分:14)
移位操作会对其操作数进行整数提升,并且在您的代码中,生成的int
会像这样转换回char
:
// first operation
a = ((a<<7)>>7); // a = (char)((a<<7)>>7);
// second operation
b <<= 7; // b = (char) (b << 7);
b >>= 7; // b = (char) (b >> 7);
引用N1570草案(后来成为C11的标准):
6.5.7按位移位运算符:
- 每个操作数应具有整数类型。
- 对每个操作数执行整数提升。结果的类型是提升的左操作数的类型。如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为未定义。
醇>
并且它认为在C99和C90中有类似的陈述。