【问题标题】:Hexadecimal representation of a negative signed 8-bit integer负符号 8 位整数的十六进制表示
【发布时间】:2021-08-13 15:54:26
【问题描述】:

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

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

这是代码的 sn-p,描述了我的问题:

#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 位十六进制?

【问题讨论】:

    标签: c hex printf


    【解决方案1】:

    当小于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);
    

    【讨论】:

    • 非常感谢您的回答。我不知道使用 printf 函数时有一个类型“转换”。我考虑了一下并尝试强制转换(int8_t),但它被覆盖了。如我所愿,您的解决方案效果很好。
    • 如果您觉得对我的问题的编辑(添加“注意:...”)过多地复制了您的最后一部分,请告诉我,我会尽力做到关于它的东西。但是当我对自己进行编辑时,我还没有(完全)阅读您的答案。
    【解决方案2】:

    有问题的“符号扩展”正在发生,因为对于您的 %X 格式说明符,预期的参数类型是 int 大小,因此您的 int_8 参数在被适当提升后(以及符号扩展),然后打印为“全尺寸”无符号整数。

    您可以通过在格式中添加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


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

    为了避免这种潜在的 UB,实现目标的更好方法是显式将您的 int8_t 参数转换为无符号等效项(相同位大小 - 或 uint8_t ,在你的情况下)。然后,当应用“默认参数提升”时,将执行 无符号扩展(因为 uint8_t 的所有可能值都可以在 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;
    }
    

    【讨论】:

    • %x 实际上期望 unsigned int 但这不是原因。整数提升作为“默认参数提升”的一部分发生,无论您为 printf 提供了什么转换说明符。
    • @Lundin 在 unsigned 部分是正确的,这就是为什么我说“... of int size 。 ..”
    • %hhx 的格式不正确 uint8_tPRIx8 是。
    • 第一段不正确。参数的提升不受格式字符串的影响。 (此时编译器甚至可能不知道该格式)。而是将 默认参数提升 应用于每个参数,而不管格式字符串如何
    • "对于您的 %X 格式说明符,预期的参数类型是 int 大小" --> C 指定 "%X" 需要匹配 unsigned,而不是 "type is int size ”。 OP 的代码,这是 UB - 一个不错的 UB。
    【解决方案3】:

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

    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

    【讨论】:

    • 省略号对应的参数没有转换。有 默认参数提升 可能会或可能不会导致有符号整数。例如,unsigned int 参数不会被提升。
    【解决方案4】:

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

    实际上,您可能(但不能保证)发现printf 函数试图从存储整数参数的位置读取unsigned int,并且您看到的内容对应于该位置的填充方式通过 int8_t 值。

    【讨论】:

      【解决方案5】:

      我想要 0x80 而不是 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 传递给 ... 参数将转换为值在 [0...255] 范围内的 int

      "%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); 
      

      【讨论】:

        猜你喜欢
        • 2014-10-19
        • 1970-01-01
        • 2018-08-30
        • 2010-09-24
        • 2021-07-12
        • 2011-02-12
        • 2014-05-05
        • 2010-11-23
        • 2014-02-20
        相关资源
        最近更新 更多