%x格式说明符,在C中带有unsigned char

时间:2011-08-25 10:37:39

标签: c

我遇到了以下示例程序,但我并不完全理解它的输出:

#include <stdio.h>

int main( void ) {

    unsigned char i, m =0xFF, n=0x1;

    for ( i = 0; i != 8; i++,n+=n, m/=2 )
        printf("%5x %5x %5x %5x %5x %5x\n", n,m,n&m,n|m,n^m,~n);

    return 0;
}

打印出来:

    1    ff     1    ff    fe fffffffe
    2    7f     2    7f    7d fffffffd
    4    3f     4    3f    3b fffffffb
    8    1f     8    1f    17 fffffff7
   10     f     0    1f    1f ffffffef
   20     7     0    27    27 ffffffdf
   40     3     0    43    43 ffffffbf
   80     1     0    81    81 ffffff7f

问题在于最后一栏。由于它是unsigned char,我希望它在每列中只打印2个位置。 ~n生成一个无符号字符,因为它的结果,但它似乎被转换为带符号的32位值并由%5x说明符扩展符号。

这怎么可能,这里发生了什么?

4 个答案:

答案 0 :(得分:10)

在算术运算中使用整数类型时会提升它们(顺便说一下,这与printf无关。)

所以,例如,

unsigned char x = 0xff;
int y = ~x; // x is promoted to 0x000000ff, then changed to 0xffffff00
unsigned char z = ~x; // truncated back to 0x00

整数促销会导致各种问题:

unsigned char x = 1;
if (x << 8)
    puts("x << 8 is true"); // does print
x <<= 8;
if (x)
    puts("x <<= 8 is true"); // does not print

截断事物的两种方法是强制转换和掩码。使用你喜欢的任何东西。

unsigned char x = 0xab;
printf("x = %02x\n", (unsigned char) x);
printf("x = %02x\n", x & 0xff);

整体推广并不总是发生,并且它不是唯一一种隐式演员。它也有点微妙,准确的规则很难记住。如果您使用的是64位数字,那么真的需要担心,因为1U << 32可能最终成为01或其他内容完全。 (在x86上经常是1

答案 1 :(得分:2)

问题在于,当你将它传递给printf的varags 时,它被提升为int - 正如Dietrich的回答 - 当你否定它时。

不幸的是,您需要将其删除为传入的字节,即(~n & 0xff)

答案 2 :(得分:0)

我的猜测是,全部传递的char被解释为32位整数,但在前4种情况下,输出是相同的。只有在最后一个中它才显示出丑陋的面孔:)

你必须屏蔽掉所有其他位,以获得像前4个例子中的结果一样的字符。

答案 3 :(得分:0)

1)您已将宽度指定为%5x,但有关C中宽度说明符的规则。如果要打印的数字的宽度大于指定的宽度,则忽略宽度说明符。因此,在最后一列中,数字内容不能在指定的宽度5中表示,因此宽度限制已被忽略。为什么宽度大于5,我已在第3点中说明。现在在第二点,我告诉%x vs unsigned char。

2)看到朋友你已经将变量'n'声明为unsigned char,但你使用的是%x,它是无符号的十六进制int。因此,在打印时,n的值被提升或技术上你可以说类型为无符号的十六进制整数。它不仅是unsigned char和%x的情况。您应该在编译器上尝试不同的组合 int a = -5; printf(“%d%u”,a,a); 现在你将获得-5%的%d和%u你将获得一些依赖于编译器属性的其他解释,无论它是16位还是32位。 现在,如果你尝试 unsigned int a = -5; printf(“%d%u”,a,a); 结果将与之前相同。在第一种情况下你写int a = 5而在第二种情况下你写无符号int = 5但结果依赖于解释所以这就是这样。

这是关于类型转换的,或者你可以说编译器的解释。当你说%x你自己说编译器将其解释为无符号的十六进制int。我希望它现在清楚,以防我不建议你运行一些不同的程序,这也是许多编译器。程序如int x = 7; printf(“%f”x);等等。你一定会明白这一点。现在我在最后一栏中解释为什么它像fffffffe一样。

3)我们考虑循环的第一次运行。这里n = 0x1。在32位编译器中,它将在内存中表示为0000 0001,因为char提供1个字节的内存。但是当您将其类型化为无符号十六进制整数时,32位编译器中的插入为0000 0000 0000 0000 0000 0000 0000 0001如果你声明int n = 1或者声明unsigned int n = 1或者你声明int n = 0x1。表示方式是一样的。即使你使用unsigned char n = 1,最右边的数字将是1而所有其他数字都为零,尽管零的数量可能更少。现在在你的循环语句中通过表示%x,你告诉编译器将内容解释为无符号的十六进制int,因此编译器将它作为十六进制提供空间,你现在通过执行此操作来执行此操作“~n”,位表示变为这样1111 1111 1111 1111 1111 1111 1111 1110.(注意 - 记住printf(“%d”,~n); case不同于printf(“%d”,n ++);在n ++情况下,变量的值在内存中也得到了更新。但是使用printf(“%d”,~n)类似于使用printf(“%d”,n + 8)。)因此,根据这个新的位表示,除了最右边之外,所有位都是1。现在当它被打印出来时,就像fffffffe一样打印出来。简单!

4)你写过“但似乎它被转换为有符号的32位值并且由%5x说明符扩展了符号。” hmmm%x不适用于带符号的十六进制int%x适用于无符号十六进制int,因此无需假设减号被截断。

5)在你的程序中,你使用'~n'现在只需检查不同的版本,如' - ~n'或  '-n'。这是出于实验目的。