如何将字节转换成十六进制数?

3
我想从单独的字节中组成数字0xAAEFCDAB。 在4个四位数前,一切都正常,但是出现了额外的4个字节。 我做错了什么?
#include <stdio.h>

int main(void) {
  unsigned long int a = 0;

  a = a | ((0xAB) << 0);
  printf("%lX\n", a);
  a = a | ((0xCD) << 8);
  printf("%lX\n", a);
  a = a | ((0xEF) << 16);
  printf("%lX\n", a);

  a = a | ((0xAA) << 24);
  printf("%lX\n", a);

  return 0;
}

输出:

image


在该平台上,long 占据了8个字节。 - phuclv
你尝试使用无符号值了吗? - Neil
3
欢迎来到符号扩展的世界。幸运的是,你没有使用0X7F或更小的第一个字节进行测试。使用0xAAUL可以解决问题,将((0xAAUL) << 24)表达式强制转换为类型unsigned long。目前,0xAA是一个int类型。 - Jonathan Leffler
不是答案,但你几乎从不想使用[unsigned] long,因为它的大小在不同操作系统上显著不可移植(在Windows上为4,在Linux上为8等)。 - HolyBlackCat
3个回答

4
C中的常量实际上是有类型的,这可能一开始并不明显,常量的默认类型是int,它是一个32位有符号整数(取决于平台,但在你的情况下很可能是这样)。在有符号数字中,最高位描述数字的符号:1为负数,0为正数(有关更多详细信息,请阅读关于二进制补码的内容)。 当执行操作 0xAB << 24时,结果是一个32位有符号值0xAB000000 ,它等于二进制中的10101011 00000000 00000000 00000000。如您所见,最高位设置为1,这意味着整个32位有符号数实际上是负数。 为了在a(一个64位无符号数)和一个32位有符号数之间执行|或操作,必须进行一些类型转换。首先执行大小提升,将32位有符号值0xAB000000升级为64位有符号值0xFFFFFFFFAB000000,根据二进制补码系统的规则。这是一个64位的有符号数,其数字值与转换前的32位有符号数相同。 之后,执行从64位有符号到64位无符号值的类型转换,以便将值与a执行OR运算。这会用1填充顶部位,并导致您在屏幕上看到的值。 为了强制常量与32位有符号int不同的类型,可以使用后缀,例如ul,如我在答案开头链接的网站中显示的。在您的情况下,ul后缀应该是最好的选择,表示64位无符号值。然后,将常量与a变量执行OR运算的代码行将类似于此: a = a | ((0xAAul) << 24);

如果你只想限制自己使用4个字节,那么一个32位无符号整数就足够存储它们了。在这种情况下,建议你将变量类型a更改为unsigned int并对常量使用u后缀。不要忘记更改printf格式以反映类型更改。 最终代码如下:

#include <stdio.h>

int main(void) {
  unsigned int a = 0;

  a = a | ((0xABu) << 0);
  printf("%X\n", a);
  a = a | ((0xCDu) << 8);
  printf("%X\n", a);
  a = a | ((0xEFu) << 16);
  printf("%X\n", a);

  a = a | ((0xAAu) << 24);
  printf("%X\n", a);

  return 0;
}

我的最后一个建议是,在需要可移植性和位数大小的情况下,不要使用默认的intlong类型。这些类型不能保证在所有平台上具有相同数量的位。相反,应该使用定义在<stdint.h>头文件中的类型,对于您的情况,可能是uint64_tuint32_t。这两个类型保证是无符号整数(它们的有符号对应类型省略了'u':int64_tint32_t),分别在所有平台上具有64位和32位的大小。关于使用它们而不是传统的intlong类型的优缺点,可以参考这个 Stack Overflow 答案。


3
a = a | ((0xAA) << 24);
((0xAA) << 24)是一个负数(它是int类型),然后被符号扩展到'unsigned long'的大小,因此在开头添加了那些0xffffffff

您需要告诉编译器您想要一个无符号的数字。

a = a | ((0xAAU) << 24);

int main(void) {
  unsigned long int a = 0;

  a = a | ((0xAB) << 0);
  printf("%lX\n", a);
  a = a | ((0xCD) << 8);
  printf("%lX\n", a);
  a = a | ((0xEF) << 16);
  printf("%lX\n", a);

  a = a | ((0xAAUL) << 24);

  printf("%lX\n", a);
  printf("%d\n", ((0xAA) << 24));

  return 0;
}

https://gcc.godbolt.org/z/fjv19bKGc


2
0xAA在进行位移时被视为有符号值。由于它的最高位是1(0xAA = 10101010b),所以在进行位移和OR操作之前,该值被符号扩展0x...FFFFFFAA
在进行位移之前,需要将0xAA转换为无符号值,这样它就会被零扩展。请注意保留HTML标签。

除非您将其声明为“signed char”,否则“0xAA”肯定不会扩展为“0xFFFFFFFFFFFFFFAA”,但默认类型为“int”。https://gcc.godbolt.org/z/dzcW1Pz6d - 0___________
请参考以下示例:https://gcc.godbolt.org/z/Gb5xfhrcc - 0___________

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接