我正在尝试理解在http://phrack.org/issues/60/10.html
中给出的代码的输出在此引用它作为参考:
#include <stdio.h>
int main(void){
int l;
short s;
char c;
l = 0xdeadbeef;
s = l;
c = l;
printf("l = 0x%x (%d bits)\n", l, sizeof(l) * 8);
printf("s = 0x%x (%d bits)\n", s, sizeof(s) * 8);
printf("c = 0x%x (%d bits)\n", c, sizeof(c) * 8);
return 0;
}
我的机器上的输出是: -
l = 0xdeadbeef (32 bits)
s = 0xffffbeef (16 bits)
c = 0xffffffef (8 bits)
以下是我的理解: -
赋值s = l,c = l将导致s和c被提升为整数,它们将分别具有l的最后16位(0xbeef)和最后8位(0xef)。
Printf尝试将上述每个值(l,s和c)解释为无符号整数(因为%x作为格式说明符传递)。从输出中我看到符号扩展已经发生。我怀疑是因为%x代表unsigned int,为什么在打印s和c时会发生符号扩展? s的输出不应该是0x0000beef而c的输出不应该是0x000000ef吗?
答案 0 :(得分:2)
为什么在打印s和c时会发生符号扩展
让我们看看以下代码:
unsigned char ucr8bit; /* Range is 0 to 255 on my machine */
signed char cr8bit; /* Range is -128 to 127 on my machine */
int i32bit;
cr8bit = MINUS_100; /* (char)(-100) or 0x9C */
i32bit = cr8bit; /* i32 bit is -100 or 0xFFFFFF9C */
正如您所看到的,尽管数字-100
是相同的,但它的表示不仅仅是0
更广泛的字符,而是可能在signed
的MSB或符号位之前。输入2s complement系统和1s complement系统。
在您的示例中,您尝试将s
和c
打印为更宽的类型,从而获得符号位复制。
此外,您的代码包含许多未定义和未指定行为的来源,因此可能在不同的编译器上提供不同的输出。
(例如,您应该使用signed char
代替char
,因为char
在某些实现上可能表现为unsigned char
,而在其他一些实施中则表现为signed char
)
l = 0xdeadbeef; /* Initializing l from an unsigned
if sizeof l is 32 bit UB as l is signed */
s = l; /* Initializing with an undefined value. Moreover
implicit conversion wider to narrower type */
printf("l = 0x%x (%d bits)\n", l, sizeof(l) * 8); /* Using %x
to print signed number and %d to print size_t */
答案 1 :(得分:1)
您使用的是32位有符号整数。这意味着只有31位可用于正数。 0xdeadbeef使用32位。因此,将其分配给32位有符号整数会使其成为负数。
当使用无符号转换说明符%x时,它看起来像是负数(带符号扩展名)。
将它复制到short或char中时,它的属性将保留为负数。
要进一步说明这一点,请尝试设置:
l = 0xef;
输出现在是:
l = 0xef (32 bits)
s = 0xef (16 bits)
c = 0xffffffef (8 bits)
0xef使用8位,当置于32位或16位变量时为正。当您将8位数字放入带符号的8位变量(char)时,您将创建一个负数。
要查看负数的保留,请尝试相反:
c = 0xef;
s = c;
l = c;
输出结果为:
l = 0xffffffef (32 bits)
s = 0xffffffef (16 bits)
c = 0xffffffef (8 bits)