如何从字节中生成十六进制数?
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) << 24)
表达式为unsigned long
类型。就目前而言,0xAA
是 int
。
不是答案,但你几乎不想使用[unsigned] long
,因为它显着的不可移植大小(Windows 上为 4,Linux 上为 8,等等)。
【参考方案1】:
C 中的常量are actually typed,起初可能并不明显,常量的默认类型是int
,它是一个有符号 32 位整数(取决于平台,但可能是您的情况)。
在有符号数字中,最高位描述数字的符号:1 为负数,0 为正数(更多详细信息,您可以阅读two's complement)。
当您执行 0xAB << 24
操作时,它会产生一个 32 位有符号值 0xAB000000
,它等于二进制中的 10101011 00000000 00000000 00000000
。可以看到,最高位设置为1,也就是说整个32位有符号数其实都是负数。
为了在a
(这是一个 64 位无符号数)和一个 32 位有符号数之间执行 |
或运算,必须执行一些类型转换。先进行大小提升,根据二进制补码系统的规则,将0xAB000000
的32位有符号值提升为0xFFFFFFFFAB000000
的64位有符号值。这是一个 64 位有符号数,与转换前的 32 位有符号数具有相同的数值值。
之后,将类型转换从 64 位有符号值转换为 64 位无符号值,以便将值与 a
进行或运算。这会用 1 填充高位,并产生您在屏幕上看到的值。
为了强制您的常量为不同于 32 位有符号 int
的类型,您可以使用诸如 u
和 l
之类的后缀,如 website I linked in the beginning of my answer 所示。在您的情况下,ul
后缀应该效果最好,表示 64 位无符号值。您的代码行与您的 a
变量或常量将看起来与此类似:
a = a | ((0xAAul) << 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;
我的最后一个建议是,当可移植性和位大小对您很重要时,不要使用默认的 int
和 long
类型。不保证这些类型在所有平台上都具有相同的位数。而是使用<stdint.h>
头文件中定义的类型,在您的情况下可能是uint64_t
或uint32_t
。这两个保证是 unsigned 整数(它们的有符号整数省略了 'u':int64_t
和 int32_t
),同时在所有平台上的大小分别为 64 位和 32 位。对于使用它们而不是传统的 int
和 long
类型的优点和缺点,我建议您参考 this Stack Overflow 答案。
【讨论】:
【参考方案2】: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
【讨论】:
【参考方案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以上是关于如何从字节中生成十六进制数?的主要内容,如果未能解决你的问题,请参考以下文章