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

时间:2015-05-31 21:08:34

标签: c

为什么使用printf在屏幕上打印字符的十六进制表示有时会打印一个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 个答案:

答案 0 :(得分:5)

char似乎已在您的系统上签名。使用标准的#2;补充&#34;整数的表示,具有最高有效位设置意味着它是负数。

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

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

答案 1 :(得分:1)

在您的计算机上,默认情况下会对char进行签名。将类型更改为unsigned char,您就会得到您期望的结果。

快速解释原因

在计算机系统中,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

我希望这个解释可以解决问题!

答案 2 :(得分:1)

char已在您的平台上签名:第6个字符的初始值设定项0x9d大于CHAR_MAX(157> 127),转换为char为负值-99(157 - 256 = -99)存储在5的偏移textStream处。

当您将textStream[5]作为参数传递给printf时,它首先会提升为int,其值为-99printf实际上需要unsigned int格式说明符{。}}。

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

为避免这种混淆,您应该将0xFFFFFFFFFFFFFF9D的操作数强制转换为printf。请尝试将此(unsigned char)替换为:

printf

答案 3 :(得分:1)

根据C标准(C11 6.3.2.1/8)对%X

的描述
  

unsigned int 参数转换为无符号八进制(o),无符号   样式dddd中的十进制(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的范围。这会导致实现定义的行为(并可能引发信号);这里最常见的实现定义是选择与char具有相同位模式的(unsigned char)0x9d

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

答案 4 :(得分:0)

这里似乎发生的是隐式char - &gt; int - &gt; 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);