负符号 8 位整数的十六进制表示

Hexadecimal representation of a negative signed 8-bit integer

在我的程序的一部分中,我将不得不管理带符号的 8 位整数。我可以使用 printf%d 格式将它们显示为小数。

但是,当涉及到十六进制表示时,负 int8_t 变量的格式不会显示为 8 位十六进制 (0xXX),而是显示为 32 -bit 十六进制 (0xFFFFFFXX).

这是描述我的问题的代码片段:

#include <stdio.h>
#include <stdint.h>

int main()
{
    int8_t value = 0;
    
    printf("t = %d = 0x%02X\n", value, value);
    t = 127;
    printf("t = %d = 0x%02X\n", value, value);
    t = -128;
    printf("t = %d = 0x%02X\n", value, value);
    
    return 0;
}

编译执行给出:

t = 0 = 0x00
t = 127 = 0x7F
t = -128 = 0xFFFFFF80

我想要 0x80 而不是 0xFFFFFF80。我做错了什么?如何将负符号8位整数显示为8位十六进制?

当小于 int 的整数类型被传递给像 printf 这样的可变参数函数时,它们 被提升 为类型 int。因此,具有值 -1 和表示 0xff 的 int8_t 变成具有值 -1 和表示 0xffffffff 的 int

这就是为什么您在使用 %x 时看到的值,它期望并打印 int。要指示您正在打印 char 值,请使用 %hhx,这会在打印前将该值转换为 char

printf("t = %d = 0x%02hhX\n", value, value);

或者,您可以将值转换为 uint8_t 以更好地匹配 %x 的预期:

printf("t = %d = 0x%02X\n", value, (uint8_t)value);

问题 'sign extension' 的发生是因为,对于您的 %X 格式说明符,预期的参数类型是 int 大小,因此您的 int_8 参数,在被适当提升(和符号扩展)之后,然后被打印为 'full-size' unsigned 整数。

你可以通过在格式中添加 hh 长度修饰符来防止后面的部分,这表明相应的参数是 char 大小,因此只会打印最不重要的字节:

#include <stdio.h>
#include <stdint.h>

int main()
{
    int8_t value = 0;

    printf("t = %d = 0x%02hhX\n", value, value);
    value = 127;
    printf("t = %d = 0x%02hhX\n", value, value);
    value = -128;
    printf("t = %d = 0x%02hhX\n", value, value);

    return 0;
}

Further Reference


注意:正如此处评论和其他答案中所指出的,%X 格式说明符(带或不带长度修饰符)用于 signed[ 的参数=45=] 类型在形式上是未定义的行为(尽管它可能适用于绝大多数现代系统)。

为了避免这种潜在的 UB,实现目标的更好方法是 明确地 将你的 int8_t 参数转换为无符号的等价物(相同的位大小——或 uint8_t,在你的情况下)。然后,当应用“默认参数提升”时,它将执行 没有符号扩展 (因为 [=18= 的所有可能值] 可表示为 int);因此,不需要将 hh 长度修饰符添加到您的格式中,因为结果 unsigned int 值的高位(添加)位将不会被设置。

此代码以明确定义的方式给出了您想要的结果:

int main()
{
    int8_t value = 0;
    printf("t = %d = 0x%02X\n", value, (uint8_t)value);
    value = 127;
    printf("t = %d = 0x%02X\n", value, (uint8_t)value);
    value = -128;
    printf("t = %d = 0x%02X\n", value, (uint8_t)value);
    return 0;
}

这是因为整数参数的默认提升。您需要使用正确的格式说明符并强制转换:

int main(void)
{
    int8_t value = 0;
    
    printf("t = %d = %02" PRIx8 "\n", value, value);
    value = 127;
    printf("t = %d = 0x%02" PRIx8 "\n", value,(uint8_t)value);
    value = -128;
    printf("t = %d = 0x%02" PRIx8 "\n", value, (uint8_t)value);
    
    return 0;
}

https://godbolt.org/z/jMqhz5156

此代码有undefined behaviour%X 格式说明符的行为仅由语言标准针对 unsigned int 类型参数的情况定义。您改为提供了 int8_t

在实践中,您可能(但不能保证)发现 printf 函数试图从存储整数参数的位置读取 unsigned int,而您所看到的对应于该位置如何被 int8_t 值填充。

I would like to get 0x80 and not 0xFFFFFF80.

0x80表示值+12810.

要将 -128 打印为 0x80 而没有 未定义的行为 ,请不要尝试使用 "%X" 打印负数。先转换为正数。

t = -128;
// printf("t = %d = 0x%02X\n", value, value); 
printf("t = %d = 0x%02X\n", value, (uint8_t) value); 

(uint8_t) value 通过添加 256 来转换 int8_t 负值。将 uint8_t 传递给 ... 参数转换为 int 范围内的值 [0 ...255].

"%X" 期望 unsigned。将 int 传递给被读取为 unsigned... 参数是可以的,只要根据 C17dr §6.5.2.2 6.

迂腐地,不要依赖 §6.5.2.2 6:

printf("t = %d = 0x%02X\n", value, (unsigned) (uint8_t) value);