如何从字节中生成十六进制数?

Posted

技术标签:

【中文标题】如何从字节中生成十六进制数?【英文标题】:How to make a hexadecimal number from bytes? 【发布时间】:2022-01-23 02:26:17 【问题描述】:

我想从单个字节组成数字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;

输出:

【问题讨论】:

long 在该平台上是 8 个字节 你用无符号值试过了吗? 欢迎来到符号扩展的世界。幸运的是,您没有使用 0X7F 或更小作为第一个字节进行测试。使用0xAAUL 可以解决问题,强制((0xAAUL) &lt;&lt; 24) 表达式为unsigned long 类型。就目前而言,0xAAint 不是答案,但你几乎不想使用[unsigned] long,因为它显着的不可移植大小(Windows 上为 4,Linux 上为 8,等等)。 【参考方案1】:

C 中的常量are actually typed,起初可能并不明显,常量的默认类型是int,它是一个有符号 32 位整数(取决于平台,但可能是您的情况)。

在有符号数字中,最高位描述数字的符号:1 为负数,0 为正数(更多详细信息,您可以阅读two's complement)。

当您执行 0xAB &lt;&lt; 24 操作时,它会产生一个 32 位有符号值 0xAB000000,它等于二进制中的 10101011 00000000 00000000 00000000。可以看到,最高位设置为1,也就是说整个32位有符号数其实都是负数。

为了在a(这是一个 64 位无符号数)和一个 32 位有符号数之间执行 | 或运算,必须执行一些类型转换。先进行大小提升,根据二进制补码系统的规则,将0xAB000000的32位有符号值提升为0xFFFFFFFFAB000000的64位有符号值。这是一个 64 位有符号数,与转换前的 32 位有符号数具有相同的数值值。

之后,将类型转换从 64 位有符号值转换为 64 位无符号值,以便将值与 a 进行或运算。这会用 1 填充高位,并产生您在屏幕上看到的值。

为了强制您的常量为不同于 32 位有符号 int 的类型,您可以使用诸如 ul 之类的后缀,如 website I linked in the beginning of my answer 所示。在您的情况下,ul 后缀应该效果最好,表示 64 位无符号值。您的代码行与您的 a 变量或常量将看起来与此类似:

a = a | ((0xAAul) &lt;&lt; 24);

或者,如果您只想将自己限制为 4 个字节,则 32 位 unsigned int 足以容纳它们。在这种情况下,我建议您将 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 类型。不保证这些类型在所有平台上都具有相同的位数。而是使用&lt;stdint.h&gt; 头文件中定义的类型,在您的情况下可能是uint64_tuint32_t。这两个保证是 unsigned 整数(它们的有符号整数省略了 'u':int64_tint32_t),同时在所有平台上的大小分别为 64 位和 32 位。对于使用它们而不是传统的 intlong 类型的优点和缺点,我建议您参考 this Stack Overflow 答案。

【讨论】:

【参考方案2】:
a = a | ((0xAA) << 24);

((0xAA) &lt;&lt; 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

【讨论】:

【参考方案3】:

0xAA 在位移期间按比例放大时被视为 有符号 值。由于它的高位为 1 (0xAA = 10101010b),因此在移位之前,缩放值将 符号扩展0x...FFFFFFAA,然后将 OR 扩展到 a

您需要将0xAA 转换为一个无符号 值,然后再对其进行位移,因此它改为零扩展

【讨论】:

0xAA 肯定不会扩展到0xFFFFFFFFFFFFFFAA,除非您将其声明为signed char,但默认类型为int gcc.godbolt.org/z/dzcW1Pz6d 或者更好的例子gcc.godbolt.org/z/Gb5xfhrcc

以上是关于如何从字节中生成十六进制数?的主要内容,如果未能解决你的问题,请参考以下文章

如何在R中生成随机的64位十六进制数?

怎么将4字节16进制转化成浮点数

c语言中如何将十六进制转换成2个字节输出

两位十六进制数由多少字节组成

怎么将4字节16进制转化成浮点数

十进制数如何换算成8421BCD码