是否使用gcc在内部使用相同的位表示int和char?

Raj*_*aja 2 c int gcc char

我正在玩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字节?

nos*_*nos 5

不。 printf 是一个可变参数函数,可变参数函数的参数将被提升为 int。在这种情况下,字符为负,因此它得到符号扩展。

  • @cnicutar 是的。同样适用于“float”,它们被提升为“double”。 (2认同)

gbu*_*mer 5

也许上面三个字节变成0xFFFFFF的原因需要更多的解释?

由于符号扩展,为*s打印的值的高三个字节的值为0xFF.

char传递给printf 的值扩展到int调用之前的值printf.

这是由于C的默认行为.

在没有signed或的情况下unsigned,编译器可以默认解释charsigned charunsigned 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"格式的解释intunsigned,无论如何,和(我的printf)没有格式打印的符号值十六进制(恕我直言,这将是一个令人困惑的).

遗憾的是,ISO/ANSI/...没有自由发布我们的编程语言标准,所以我不能指出规范,但搜索网络可能会出现工作草案.我没有试过找到它们.我推荐Samuel P. Harbison和Guy L. Steele撰写的"C:A参考手册"作为ISO文件的更便宜的替代品.

HTH