我正在玩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;
}
Run Code Online (Sandbox Code Playgroud)
用gcc编译并输入'cent'符号(hex:c2 a2)后,得到以下输出
$ ./a.out
¢
ffffffc2: ?
ffffffa2: ?
a:
Run Code Online (Sandbox Code Playgroud)
因此,不仅仅打印c2和a2,我得到了整个4个字节,好像它是一个int类型.
这是否意味着char的长度不是1字节,ascii使它看起来像1字节?
不。 printf 是一个可变参数函数,可变参数函数的参数将被提升为 int。在这种情况下,字符为负,因此它得到符号扩展。
也许上面三个字节变成0xFFFFFF的原因需要更多的解释?
由于符号扩展,为*s打印的值的高三个字节的值为0xFF.
char传递给printf 的值扩展到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;
}
Run Code Online (Sandbox Code Playgroud)
打印:
schar[0]=70 schar[1]=ffffff80 uchar[0]=70 uchar[1]=80
Run Code Online (Sandbox Code Playgroud)
该char 值由我的(Mac的gcc)编译器解释为signed char,因此编译器生成用于签名的代码扩展char到printf调用int 之前.
如果signed char值的顶部(符号)位设置为(\ x80),则转换为intsign会扩展该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无符号扩展名.
您可以通过使用合适的格式(%hhx)强制printf仅打印int的低字节,因此这只能正确打印原始char中的值:
/* 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;
}
Run Code Online (Sandbox Code Playgroud)
这打印:
schar[0]=70 schar[1]=80 uchar[0]=70 uchar[1]=80
Run Code Online (Sandbox Code Playgroud)
因为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