首页 文章

为什么打印char有时会在C中打印4个字节的数字

提问于
浏览
3

为什么使用printf在屏幕上打印十六进制char表示有时会打印一个4字节的数字?

这是我写的代码

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

int main(void) {
    char testStream[8] = {'a', 'b', 'c', 'd', 0x3f, 0x9d, 0xf3, 0xb6};

   int i;
    for(i=0;i<8;i++){
      printf("%c = 0x%X, ", testStream[i], testStream[i]);
    }

    return 0;
}

以下是输出:

a = 0x61, b = 0x62, c = 0x63, d = 0x64, ? = 0x3F, � = 0xFFFFFF9D, � = 0xFFFFFFF3, � = 0xFFFFFFB6

5 回答

  • 1

    char 似乎已在您的系统上签名 . 使用整数的标准"two's complement"表示,具有最高有效位设置意味着它是负数 .

    为了将 char 传递给像 printf 这样的vararg函数,它必须扩展为 int . 要保留其值,符号位将复制到所有新位( 0x9D0xFFFFFF9D ) . 现在 %X 转换期望并打印 unsigned int ,您可以看到负数中的所有设置位而不是减号 .

    如果您不想这样,则必须使用 unsigned char 或将其传递给 unsigned char ,并将其传递给 printf . 与 signed char 相比, unsigned char 没有额外的位,因此具有相同的位模式 . 当无符号值被扩展时,新位将为零,并且您首先得到预期的值 .

  • 5

    从C标准(C11 6.3.2.1/8) %X 的描述:

    unsigned int参数在样式dddd中转换为无符号八进制(o),无符号十进制(u)或无符号十六进制表示法(x或X);字母abcdef用于x转换,字母ABCDEF用于X转换 .

    您没有提供 unsigned int 作为参数1,因此您的代码会导致未定义的行为 .

    在这种情况下,未定义的行为表现为 printf 的实现,为 %X 编写代码,就像你只传递 unsigned int 一样 . 您所看到的是 unsigned int 值,其具有与您作为参数给出的负整数值相同的位模式 .


    还有另一个问题,有:

    char testStream[8] = {'a', 'b', 'c', 'd', 0x3f, 0x9d, 0xf3, 0xb6};
    

    在您的系统上, char 的范围是 -128+127 . 但是 0x9d ,即 157 ,超出了 char 的范围 . 这会导致实现定义的行为(并可能引发信号);这里最常见的实现定义是将选择与 (unsigned char)0x9d 具有相同位模式的 char .


    1虽然它表示 unsigned int ,但此部分通常被解释为表示允许带符号的 int 或任何较低等级的参数,并且具有非负值 .

  • 0

    在您的计算机上,默认情况下会签署 char . 将类型更改为 unsigned char ,您将获得您期望的结果 .

    A Quick explanation on why this is

    在计算机系统中,MSB(最高有效位)是具有最高值的位(最左位) . 数字的MSB用于确定数字是正数还是负数 . 即使 char 类型长度为8位, signed char 也只能使用7位,因为第8位确定其是正还是负 . 这是一个例子:

    Data Type: signed char
      Decimal: 25
       Binary: 00011001
               ^
               |
               --- Signed flag. 0 indicates positive number. 1 indicates negtive number
    

    因为 signed char 使用第8位作为有符号标志,所以它实际用于存储数字的位数是7位 . 您可以以7位存储的最大值是127( 7F ,十六进制) .

    为了将数字从正数转换为负数,计算机使用称为二重符号的东西 . 工作原理是所有位都被反转,然后 1 被添加到值中 . 这是一个例子:

    Decimal: 25
     Binary: 00011001
    
    Decimal: -25
     Binary: 11100111
    

    当你声明 char testStream[8] 时,编译器假设你想要 signed char . 当您分配了 0x9D0xF3 的值时,这些数字大于 0x7F ,这是可以容纳7位有符号字符的最大数字 . 因此,当您尝试 printf 屏幕的值时,它会扩展为 int 并填充 FF .

    我希望这个解释能够解决问题!

  • 1

    char 在您的平台上签名:第6个字符的初始化程序 0x9d 大于 CHAR_MAX (157> 127),它被转换为 char 作为负值 -99 (157 - 256 = -99)存储在 textStream 中的偏移 5 处 .

    textStream[5] 作为参数传递给 printf 时,它首先被提升为 int ,值为 -99 . printf 实际上希望 "%X""%X" 格式说明符 .

    在您的体系结构上, int 是32位,2的补码表示负值,因此 -99 作为 int 传递的值被解释为 4294967197 (2 ^ 32-99),其十六进制表示为 0xFFFFFF9D . 在不同的架构上,它可能是别的东西:在16位DOS上,你会得到 0xFF9D ,在64位Cray上你可能得到 0xFFFFFFFFFFFFFF9D .

    为避免这种混淆,您应该将 printf 的操作数转换为 (unsigned char) . 尝试用此替换你的 printf

    printf("%c = 0x%2X, ", (unsigned char)testStream[i], (unsigned char)testStream[i]);
    
  • 1

    这里似乎发生的是隐式char - > int - > uint cast . 当正char被转换为int时,没有任何不好的事情发生 . 但是如果负的字符如0x9d,0xf3,0xb6转换为int将使它们保持负值,因此它们变为0xffffff9d,0xfffffff3,0xffffffb6 . 不是实际值没有改变,即0xffffff9d == -99和0x9d == -99 . 要正确打印它们,您可以将代码更改为

    printf("%c = 0x%X, ", testStream[i] & 0xff, testStream[i] & 0xff);
    

相关问题