打印值0x89时的奇怪输出(-119)

正如标题所说,运行以下代码时,我得到一个“奇怪”的结果:

#include  int main() { char buff[4] = {0x17, 0x89, 0x39, 0x40}; unsigned int* ptr = (unsigned int*)buff; char a = (char)((*ptr <> (3*8)); char b = (char)((*ptr <> (3*8)); char c = (char)((*ptr <> (3*8)); char d = (char)((*ptr <> (3*8)); printf("0x%x\n", *ptr); printf("0x%x\n", a); printf("0x%x\n", b); printf("0x%x\n", c); printf("0x%x\n", d); return 0; } 

输出:

 0x40398917 0x40 0x39 0xffffff89 0x17 

为什么我没有得到0x89

这是因为您的char变量已签名,并且在升级时它们正在进行符号扩展(在这种情况下升级为更宽的类型)。 符号扩展是一种在执行此促销时保留符号的方式,因此-119保持为-119无论是8位,16位还是更宽的类型。

您可以通过显式使用unsigned char来修复它,因为至少在C中, char是有符号还是无符号是特定于实现的。 从C11 6.2.5 Types /15

实现应将char定义为具有与signed char或unsigned char相同的范围,表示和行为。

对于无符号类型,符号扩展不起作用,因为它们是……好吧,未签名:-)

默认情况下,char是签名的 – 这意味着数字从-128到127运行。除此之外的任何数字都不适合。 如果您将char更改为unsigned char ,您将获得所需的数字。

使用memcpy而不是强制转换

 char buff[4] = {0x17, 0x89, 0x39, 0x40}; unsigned int* ptr = (unsigned int*)buff; 

这是不正确的buff不指向int对象或数组,因此未定义(unsigned int*)buff

buff重新解释为unsigned int的安全方法是使用memcpy

 char buff[4] = {0x17, 0x89, 0x39, 0x40}; unsigned int ui; assert (sizeof ui == sizeof buff); memcpy (buff, &ui, sizeof ui); 

使用memcpy ,您无法确保复制的位表示对目标类型有效,当然。

一种可移植但退化的方法是检查表示是否与现有对象匹配(注意,以下是愚蠢的代码):

 char *null_ptr = 0; char null_bytes[sizeof null_ptr] = {0}; if (memcmp (null_ptr, null_bytes, sizeof null_bytes)==0) { char *ptr2; memcpy (null_bytes, ptr2, sizeof null_bytes); assert (ptr2 == 0); } 

此代码使用memcpy并具有完全定义的行为(即使无用)。 OTOH,行为

 int *ptr3 = (int*)null_bytes; 

未定义,因为null_bytes不是intunsigned int的地址。