非ASCII字符声明

4

我希望存储一个字符(以便与其他字符进行比较)。

如果我像这样声明变量:

char c = 'é';

一切都运行良好,但我收到了这些警告:
warning: multi-character character constant [-Wmultichar]
   char c = 'é';
            ^
ii.c:12:3: warning: overflow in implicit constant conversion [-Woverflow]
   char c = 'é';

我认为我理解了为什么会有这些警告,但我想知道为什么它还能工作呢?而且,我是否应该像这样定义:int d = 'é'; 即使它需要更多的内存空间?此外,我还得到了以下声明的警告:

warning: multi-character character constant [-Wmultichar]

int d = 'é';

我有遗漏吗?谢谢;)


它是如何工作的?'é'是否等于例如169(这不是您想要的,我猜测)? - mafso
这段代码:char d = 'é'; char *w = "aaéaéaa"; while (*w++) { if (d == *w) printf("ok\n"); }输出 ok ok - Simon
在我的平台上,使用char *w = "aa\xa9" "aéaa";会得到相同的输出(0xA9是169,不是é的utf-8编码)。换句话说:如果某个字节和'é'不相等,则它们肯定不相同,但反之则不一定成立。而且你所做的事情没有可移植性的可重复结果:给出多个字符的字符常量的值是_实现定义的_。 - mafso
好的,谢谢您的澄清,但是对于您来说,什么是最好的方法呢? - Simon
2个回答

3

尝试使用 wchar_t 而不是 char。对于 ASCII 码,char 是一个字节,但对于多字节字符集(如 UTF-8),它是不合适的。此外,将您的字符文字标记为宽字符而非窄字符:

#include <wchar.h>
...
wchar_t c = L'é';

"string" 怎么声明? - user3553031
char d = 'é'; char *w = "aaéaéaa"; while (*w++){if (d == *w) printf ("ok\n");} - Simon
使用窄字符、窄字符指针、窄字符字面值和窄字符字符串字面值无法处理宽字符数据。请改用以下代码:wchar_t d = L'é'; wchar_t *w = L"aaéaéaa"; while (*w++){if (d == *w) printf ("ok\n");} - user3553031
2
strtok 适用于窄字符字符串。尝试使用 wcstok。您不能混合使用窄字符和宽字符的内容,然后期望其正常工作。 - user3553031
1
使用 fgetws 处理宽字符。在宽字符函数的名称中,总是有一个 "w"。不幸的是,它并不总是出现在同样的位置。 - user3553031
显示剩余4条评论

2
"

é" 的 Unicode 代码点是 0xE9,UTF-8 编码为 "\"\xc3\xa9\""。

我假设你的源文件编码是 UTF-8,所以

"
char c = 'é';

大致相当于。
char c = '\xc3\xa9';

这段文字描述了对于多字符常量的处理方法是由实现定义的。在 GCC 中,编译器会逐个字符地评估一个多字符常量,将前一个值左移目标字符每个位的数量,并通过新字符的位模式截断为目标字符的宽度进行或运算。最终的位模式具有 int 类型,因此无论单个字符是否带符号,它都是有符号的(这与 GCC 版本 3.1 及更早版本略有不同)。如果常量中的字符数超过了目标 int 的容量,编译器会发出警告并忽略多余的前导字符。例如,在一个具有 8 位 char 的目标中,'ab' 将被解释为 (int) ((unsigned char) 'a' * 256 + (unsigned char) 'b'),而 '\234a' 则被解释为 (int) ((unsigned char) '\234' * 256 + (unsigned char) 'a')
因此,'é' 的值为0xC3A9,它适合于int(至少适用于32位int),但不适合于(8位)char,因此将其转换为char的方式再次是实现定义

对于转换为宽度为N的类型,该值被模2 N 减少以使其在类型范围内; 不发出信号。

这给出了(使用有符号char

#include <stdio.h>
int main(void) {
    printf("%d %d\n", 'é', (char)'é');
    if((char)'é' == (char)'©') puts("(char)'é' == (char)'©'");
}

输出:
50089 -87
(char)'é' == (char)'©'

"50089是0xC3A9,87是0xA9。因此,当将é存储到char中时,会丢失信息(有些字符如©与é相等)。您可以"
使用,这是一个实现相关的宽字符类型,在Linux上占用UTF-32的4个字节:wchar_t c = L'é';。你可以通过将它们转换为区域特定的多字节编码(可能是UTF-8,但你需要先设置区域设置,参见setlocale;请注意,更改区域设置可能会更改函数如isalphaprintf的行为),或者直接使用宽字符串(使用前缀获取宽字符字符串文字)。
使用字符串并在其中存储UTF-8(例如const char *c = "é";const char *c = "\u00e9";const char *c = "\xc3\xa9;",具有不同的语义;对于C11,也可以查找UTF-8字符串文字u8前缀)。
请注意,文件流具有方向(参见)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接