我正在玩unicode字符(不使用wchar_t支持)只是为了好玩。我只使用常规字符数据类型。我注意到,当它们以十六进制打印时,它们显示的是完整的4个字节而不是仅仅一个字节。
对于前。考虑这个c文件:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *s = (char *) malloc(100);
fgets(s, 100, stdin);
while (s && *s != '\0') {
printf("%x\n", *s);
s++;
}
return 0;
}
用gcc编译并输入'cent'符号(hex:c2 a2)后,我得到以下输出
$ ./a.out
¢
ffffffc2: ?
ffffffa2: ?
a:
因此,不仅仅打印c2和a2,我得到了整个4个字节,就像它是一个int类型一样。
这是否意味着char的长度不是1字节,ascii使它看起来像1字节?
答案 0 :(得分:5)
也许上面三个字节变成0xFFFFFF的原因需要更多解释?
由于符号扩展,为* s打印的值的高三个字节的值为0xFF。
传递给printf的char
值在调用int
之前会扩展为printf
。
这是由于C的默认行为。
如果没有signed
或unsigned
,编译器可以默认将char
解释为signed char
或unsigned char
。除非使用命令行选项或编译指示明确更改,否则它始终是一个或另一个。在这种情况下,我们可以看到它是signed char
。
在没有更多信息(原型或演员表)的情况下,C传递:
int
,因此char
,short
,unsigned char
unsigned short
会转换为int
。它永远不会传递char,unsigned char,signed char,作为单个字节,它总是传递int
。unsigned int
与int
的大小相同,因此值不会更改编译器需要决定如何将较小的值转换为int
。
signed
值:int
的高位字节是从较小的值扩展的符号,有效地复制顶部符号位向上以填充int
。如果较小的有符号值的最高位为0,则高位字节用0填充。如果较小的有符号值的最高位为1,则高位字节用1填充。因此printf(“%x”,* s )打印ffffffc2 unsigned
值未进行符号扩展,int的高位字节为“零填充”因此,C可以在没有原型的情况下调用函数(尽管编译器通常会对此进行警告)
所以你可以写,并期望这个运行(虽然我希望你的编译器发出警告):
/* Notice the include is 'removed' so the C compiler does default behaviour */
/* #include <stdio.h> */
int main (int argc, const char * argv[]) {
signed char schar[] = "\x70\x80";
unsigned char uchar[] = "\x70\x80";
printf("schar[0]=%x schar[1]=%x uchar[0]=%x uchar[1]=%x\n",
schar[0], schar[1], uchar[0], uchar[1]);
return 0;
}
打印:
schar[0]=70 schar[1]=ffffff80 uchar[0]=70 uchar[1]=80
我的(Mac的gcc)编译器将char
值解释为signed char
,因此编译器生成代码以将char
扩展为int
在 printf调用之前。
如果签名的char值设置了顶部(符号)位(\ x80),则转换为int
符号会扩展char
值。符号扩展用1填充高位字节(在这种情况下是3个字节以产生4个字节int
),由printf打印为ffffff80
如果签名的char值的顶部(符号)位清除(\ x70),则转换为int
仍然符号会扩展char
值。在这种情况下,符号为0,因此符号扩展名用0填充高位字节,由printf打印为70
我的示例显示了值为unsigned char
的情况。在这两种情况下,值不会符号扩展,因为值为unsigned
。相反,它们使用0填充扩展为int。它可能看起来像printf只打印一个字节,因为该值的相邻三个字节将为0.但它打印整个int
,它发生的值是0x00000070和0x00000080,因为unsigned char
价值转换为
int
没有符号扩展名。
您可以强制printf仅使用合适的格式(%hhx)打印int的低字节,因此这只会正确打印原始字符中的值:
/* Notice the include is 'removed' so the C compiler does default behaviour */
/* #include <stdio.h> */
int main (int argc, const char * argv[]) {
char schar[] = "\x70\x80";
unsigned char uchar[] = "\x70\x80";
printf("schar[0]=%hhx schar[1]=%hhx uchar[0]=%hhx uchar[1]=%hhx\n",
schar[0], schar[1], uchar[0], uchar[1]);
return 0;
}
打印:
schar[0]=70 schar[1]=80 uchar[0]=70 uchar[1]=80
因为printf解释%hhx将int视为unsigned char
。这并没有改变在调用printf之前将char符号扩展为int的事实。它只是一种告诉printf如何解释int内容的方法。
在某种程度上,对于signed char *schar
,%hhx
的含义看起来有些误导,但'%x'格式无论如何都将int
解释为unsigned
,并且我的printf)没有格式打印十六进制用于签名值(恕我直言,这将是一个令人困惑的)。
可悲的是,ISO / ANSI / ...没有自由发布我们的编程语言标准,所以我不能指出规范,但搜索网络可能会出现工作草案。我没有试过找到它们。我推荐Samuel P. Harbison和Guy L. Steele撰写的“C:A参考手册”作为ISO文件的更便宜的替代品。
HTH
答案 1 :(得分:4)
没有。 printf是一个变量参数函数,变量参数函数的参数将被提升为int。在这种情况下,char是负数,因此它会被符号扩展。
答案 2 :(得分:1)
%x
告诉printf要打印的值是无符号int
。因此,它会将char
提升为unsigned int
,根据需要进行扩展,然后打印出结果值。