ece*_*ulm 34 c bit-manipulation
以下程序
#include <inttypes.h> /* printf(" %" PRIu32 "\n"), my_uint32_t) */
#include <stdio.h> /* printf(), perror() */
int main(int argc, char *argv[])
{
uint64_t u64 = ((unsigned char)0x80) << 24;
printf("%" PRIX64 "\n", u64);
/* uint64_t */ u64 = ((unsigned int)0x80) << 24;
printf("%016" PRIX64 "\n", u64);
}
Run Code Online (Sandbox Code Playgroud)
产生
FFFFFFFF80000000
0000000080000000
Run Code Online (Sandbox Code Playgroud)
是什么区别((unsigned char)0x80),并((unsigned int)0x80)在这方面?
我想这(unsigned char)0x80会被提升到(unsigned char)0xFFFFFFFFFFFFFF80然后有点转移,但为什么这种转换认为unsigned char是签名?
值得注意的是,它0x80 << 16会产生预期的结果0x0000000000800000.
das*_*ght 29
C编译器在执行shift之前执行整数提升.
该标准的规则6.3.1.1说:
如果a
int可以表示原始类型的所有值,则该值将转换为int; 否则,它被转换为unsigned int.这些被称为整数促销.
由于所有值unsigned char都可以表示int,因此0x80转换为有符号int.同样不是这样的unsigned int:它的一些值不能表示为a int,所以unsigned int在应用整数提升后它仍然存在.
oua*_*uah 21
运算<<符的左操作数经历整数提升.
(C99,6.5.7p3)"对每个操作数执行整数提升."
这意味着这个表达式:
((unsigned char)0x80) << 24
Run Code Online (Sandbox Code Playgroud)
相当于:
((int) (unsigned char)0x80) << 24
Run Code Online (Sandbox Code Playgroud)
相当于:
0x80 << 24
Run Code Online (Sandbox Code Playgroud)
它int在32位int系统中设置了一个符号位.然后,当在声明中0x80 << 24转换为符号扩展名时,将产生该值.uint64_tu640xFFFFFFFF80000000
编辑:
请注意,正如Matt McNabb在注释中正确添加的那样,技术上会0x80 << 24在C中调用未定义的行为,因为结果在<<左操作数的类型中无法表示.如果您正在使用gcc,当前的编译器版本保证它当前不会使此操作未定义.
转换的奇怪部分发生在将<<int32的结果转换为uint64时.您正在使用32位系统,因此整数类型的大小为32位.以下代码:
u64 = ((int) 0x80) << 24;
printf("%llx\n", u64);
Run Code Online (Sandbox Code Playgroud)
打印:
FFFFFFFF80000000
Run Code Online (Sandbox Code Playgroud)
因为(0x80 << 24)给出的0x8000000是-2147483648的32位表示.通过乘以符号位将该数字转换为64位,并给出0xFFFFFFFF80000000.
你目睹的是未定义的行为.C99§6.5.7/ 4描述左移这样:
结果
E1 << E2是E1左移位E2位置; 腾出的位用零填充.如果E1具有无符号类型,则结果的值为E1×2E2,比结果类型中可表示的最大值减少一个模数.如果E1有一个带符号的类型和非负值,并且结果类型中可以表示E1×2E2,那么这就是结果值; 否则,行为未定义.
在您的情况下,E1值为128,其类型int不是unsigned char.至于其他的答案也提到,该值将被提升到int前评估.涉及的操作数是有符号的int,128位左移24位的值是2147483648,这比系统上可表示的最大值多一个int.因此,程序的行为是不确定的.
为了避免这种情况,你可以确保的类型E1是unsigned int按类型,铸造,与其到unsigned char.