我遇到了以下示例程序,但我并不完全理解它的输出:
#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
说明符扩展符号。
这怎么可能,这里发生了什么?
答案 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
可能最终成为0
或1
或其他内容完全。 (在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'。这是出于实验目的。