打印每个宽字符字节的字符值

printing the char value of each wide character's bytes

当 运行 以下情况时:

char acute_accent[7] = "éclair";
int i;
for (i=0; i<7; ++i)
{
    printf("acute_accent[%d]: %c\n", i, acute_accent[i]);
}

我得到:

acute_accent[0]: 
acute_accent[1]: �
acute_accent[2]: c
acute_accent[3]: l
acute_accent[4]: a
acute_accent[5]: i
acute_accent[6]: r

这让我觉得多字节字符 é 是 2 字节宽的。

然而,当 运行 这个(忽略编译器从 multi-character character constant 警告我之后):

printf("size: %lu",sizeof('é'));

我得到 size: 4

大小不同的原因是什么?

编辑:这个问题与 this 不同,因为它更多地是关于多字节字符编码、不同的 UTF 及其大小,而不是仅仅理解字符的大小。

来自 C99 standard,第 6.4.4.4 节:

2 An integer character constant is a sequence of one or more multibyte characters enclosed in single-quotes, as in 'x'.

...

10 An integer character constant has type int.

sizeof(int) 在您的计算机上可能是 4,这就是您得到该结果的原因。

所以'é''c''l'都是整型字符常量,所以都是int类型,大小都是4。事实上有些是多字节的而有的不在这方面并不重要。

您看到差异的原因是因为在您的第一个示例中,字符 é 被编译器编码为两字节 UTF-8 代码点 0xC3 0xA9.

看这里:

http://www.fileformat.info/info/unicode/char/e9/index.htm

并且正如 dbush 所描述的那样,字符 'é' 被编码为 UTF-32 代码点并以整数形式存储;因此它被表示为四个字节。

您的部分困惑源于通过以未定义的方式存储 Unicode 来使用实现定义的功能。

为防止未定义的行为,您应该始终清楚地识别字符串文字的编码类型。

例如:

char acute_accent[7] = u8"éclair"

这是非常糟糕的形式因为除非你自己算出来,否则你无法知道字符串的确切长度。事实上,我的编译器 (g++) 对我大吼大叫,因为虽然字符串是 7 个字节,但它总共有 8 个字节,最后是空字符。所以你实际上已经超出了缓冲区。

改用这个更安全:

const char* acute_accent = u8"éclair"

注意你的字符串实际上是 8 个字节:

#include <stdio.h>
#include <string.h> // strlen

int main() {
    const char* a = u8"éclair";

    printf("String length : %lu\n", strlen(a));

    // Add +1 for the null byte
    printf("String size   : %lu\n", strlen(a) + 1);

    return 0;
}

输出为:

String length : 7
String size   : 8

另请注意 C 和 C++ 之间的字符大小不同!!

#include <stdio.h>

int main() {
    printf("%lu\n", sizeof('a'));

    printf("%lu\n", sizeof('é'));

    return 0;
}

在 C 中输出是:

4
4

而在 C++ 中,输出是:

1
4