【Windows编程】杂记
在Visual Studio中设置项目属性的字符集。如果设置为“使用 Unicode 字符集“这会在编译器中添加 Unicode 宏,如果设置为 ”使用多字节字符集“ 这会在编译器中定义 _MBCS(不定义 UNICODE/_UNICODE)。
而 Windows 提供的 API 会根据是否定义了 Unicode 宏 将函数进行转变,比如 CreateMutex
1 |
后缀 W 表示宽字符,说明这个函数接收的字符串必须是宽字符wchar_t类型的,接收的字符串编码是宽字符编码。
宽字符仅代表一个字符所占的字节数,具体的编码由用户和编译器决定。
比如说用户定义wchar_t* str = L"你好;",MSVC看到wchar_t会给每个字符分配2个字节,看到前缀L会编码成UTF-16;GCC会给每个字符分配4个字节,编码成UTF-32
| 平台 | wchar_t 大小 |
实际编码 |
|---|---|---|
| Windows (MSVC) | 2 字节 | UTF-16(支持代理对) |
| Linux/macOS (GCC/Clang) | 4 字节 | UTF-32(UCS-4) |
| 类型 | 前缀 | 字符类型 | 编码(常见) |
|---|---|---|---|
| 普通字符串 | 无 | char |
ASCII / UTF-8(源码编码) |
| 宽字符串 | L |
wchar_t |
UTF-16(MSVC) 或 UTF-32(GCC/Clang) |
后缀 A 表示ANSI,表示这个函数接收的字符串编码必须是ANSI编码(本地代码页 ,如中文GBK)。不同的编译器对char* str = “你好”字符串的编码会有自己的默认行为。
比如MSVC默认会把str用本地代码页 (如中文GBK)编码,所以用Visual Studio编译这个函数用char*就可以传入参数,因为MSVC就是把char*字符串用ANSI编码。
但如果用GCC编译可能就会出错,因为gcc会默认将char*编码为UTF-8而这个函数只能处理ANSI编码。
所谓ANSI是Windows自己提出的概念,它实际上是 系统的本身的编码。也就是说,如果系统用的是GBK编码,那ANSI就是GBK;如果系统是Big5,那ANSI就是Big5。总的来说,ANSI是很多编码的统称。
实际上,Windows 内核底层只实现 W 版本(只能处理UTF-16编码字符串),A 版本只是一个转换层,它会:
- 将 ANSI 字符串转换为 UTF-16
- 调用对应的
W函数 - 将结果转换回 ANSI(如果需要)
编译器如何编码字符串存储到可执行文件中
Tip: 这里说的编译器对字符编码都是类似char* str=”你好”,不加前缀的字符串编码
解码源文件(怎么读)和编码字符串(char)*(怎么存)是两套独立的逻辑,分别由 -finput-charset 和 -fexec-charset 控制,互不干扰。编译器不会“继承”或“记住”源文件的编码方式去编码字符串,它只看你给它的选项。
两大主流编译器的默认行为
| 编译器 | -finput-charset 默认值 |
-fexec-charset 默认值 |
|---|---|---|
| GCC / Clang (Linux, macOS) | UTF-8 | UTF-8 |
| MSVC (Windows) | 本地代码页 (如中文GBK) | 本地代码页 (如中文GBK) |
当你“正常使用”而没有指定任何选项时:
- 在 Linux 上用 GCC:编译器默认按 UTF-8 去读你的源文件,也用 UTF-8 存你的字符串。两套逻辑一致,且都是 UTF-8。
- 在 Windows 上用 MSVC:编译器默认按系统的本地代码页(比如 GBK)去读你的源文件,也用同样的 GBK 存你的字符串。两套逻辑也一致,但取决于你系统的区域设置。
所以,回到你最开始的疑问——“编译器根据什么来编码字符串”——答案是:在不指定选项时,它依据的就是编译器自己内置的默认规则,GCC 是 UTF-8,MSVC 是本地代码页。
