我一直在try 使用write()函数写入非ASCII字符,如ğçşstdout.当我试图将它们从字符串文字写到stdout时,有件事把我搞糊涂了.

#include <unistd.h>

int main()
{
    char *str = "ğğğ";
    
    write(1, &str[0], 1);
    write(1, &str[1], 1);
}

在程序中,我预计它应该首先将'ğ'个字符8位写入stdout,然后再写入第二个8位,因为存在2个'ğ'字符的字节.然而,它只将'ğ'个字符写入stdout.我想不出它是如何组合两个不同的写入函数输出的.当我使用printf函数时,同样的事情也会发生.

#include <stdio.h>

int main()
{
    char *str = "ğğğ";
    
    printf("%c", str[0]);
    printf("%c", str[1]);
}

在该代码中,它还打印一个'ğ'个字符.

当我try 打印其他多字节字符时,也会发生同样的情况.这一切为什么要发生?我错过了什么非常简单的东西吗?

推荐答案

在C源代码中、在过程存储器中和/或作为go 往/来自终端的字节流对非ASCII字符进行编码有不同的方法.假设您的系统对所有3个字符都使用UTF-8,这是非常常见的,尤其是在Unix系统(Linux、MacOS)上,字符ğ被编码为一个2字节的序列C4 9F,因此字符串"ğğğ"实际上占用6个字节外加一个空终止符.

要打印单个ğ,您可以使用write(1, "ğ", 2)或简单地使用write(1, "ğ", strlen("ğ")).

当您对位于str[0]str[1]的各个字节发出两个单独的写入调用时,第一个将输出UTF-8前导字节0xC4,第二个将输出尾部字节0x9F.str[1]不是字符串中的第二个character,它表示总共具有7个字节的字符数组str的第二个字节.在一个或两个WRITE调用中写入2个字节的序列在您的终端上产生相同的结果:单个ğ个字符.

printf个调用的行为完全相同:输出2个字节,形成一个字符ğ(Unicode代码点U-011F,编码为"\xC4\x9F").

请注意,'ğ'是格式错误的字符常量,它不可移植,不应用于表示非ASCII字符.一些C编译器会将其解析为多字节字符常量,如'abc',这在历史上是一个令人困惑的奇怪现象,而另一些C编译器,如clang,将生成错误.

以下是一个测试程序:

#include <stdio.h>
#include <string.h>
#include <wchar.h>

int main(void) {
    const char *str = "ğ";
    const wchar_t *wstr = L"ğ";

    printf("str: \"%s\"\n", str);
    printf("strlen(\"%s\"): %zu\n", str, strlen(str));
    printf("sizeof \"%s\": %zu\n", "ğ", sizeof "ğ");
    printf("str[0]: 0x%02hhX, str[1]: 0x%02hhX\n", str[0], str[1]);

    //this generates an error:
    //  character too large for enclosing character literal type
    //printf("'%s' = %#x\n", str, 'ğ');

    printf("wstr: L\"%s\"\n", str);
    printf("wclen(L\"%s\"): %zu\n", str, wcslen(wstr));
    printf("sizeof L\"%s\": %zu\n", str, sizeof L"ğ");
    printf("wstr[0]: 0x%04X\n", wstr[0]);

    printf("sizeof L\'%s\': %zu\n", str, sizeof L'ğ');
    printf("L'%s' = 0x%04X\n", str, L'ğ');

    return 0;
}

MacOS上的输出:

str: "ğ"
strlen("ğ"): 2
sizeof "ğ": 3
str[0]: 0xC4, str[1]: 0x9F
wstr: L"ğ"
wclen(L"ğ"): 1
sizeof L"ğ": 8
wstr[0]: 0x011F
sizeof L'ğ': 4
L'ğ' = 0x011F

C++相关问答推荐

为什么已经设置的值在C中被重置为for循环条件中的新值?

字符数组,字符指针,在一种情况下工作,但在另一种情况下不工作?

malloc实现:判断正确的分配对齐

在函数中使用复合文字来初始化C语言中的变量

手动矢量化性能差异较大

*p[num]和(*p)num的区别

使用NameSurname扫描到两个单独的字符串

我编译了一个新的c程序,并收到以下错误

C:二进制搜索和二进制插入

难以理解Makefile隐含规则

如何在下面的C代码中正确管理内存?

将返回的char*设置为S在函数中定义的字符串文字可能会产生什么问题?

在C中访问数组中的特定值

C语言中MPI发送接收字符串时出现的分段错误

从C中的函数返回静态字符串是不是一种糟糕的做法?

不带Malloc的链表

我编写这段代码是为了判断一个数字是质数、阿姆斯特朗还是完全数,但由于某种原因,当我使用大数时,它不会打印出来

在我的第一个C语言中观察到的错误';你好世界';程序

在NASM中链接Linux共享库时出错-';将R_ X86_64_;foo';

在 C23 之前如何对空指针使用nullptr?