提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
-
-
- 核心前提:编译器处理字符的"统一中转规则"
- [第一步:source_character_set 解码 → 得到 Unicode 码点(核心:统一转Unicode)](#第一步:source_character_set 解码 → 得到 Unicode 码点(核心:统一转Unicode))
-
- [关键结论:解码后得到的一定是 Unicode 码点,而非GBK/UTF-8的"码点"](#关键结论:解码后得到的一定是 Unicode 码点,而非GBK/UTF-8的“码点”)
- 分步拆解(用"输"字举例)
- [第二步:Unicode码点 → 按 execution_character_set 编码 → 生成程序中的字节序列](#第二步:Unicode码点 → 按 execution_character_set 编码 → 生成程序中的字节序列)
- 完整流程示例:"输入"字符串的全转换过程
- 总结
-
c
源文件中常量字符串 如"输入属性配置" 这个常量字符串的字节序列
根据source_character_set 解码得到码点过程,得到的都是Unicode码点是吗,
比如源文件问gbk,那么这个字节序列就是gbk按照gbk解码得到的不是gbk码点,还是得到Unicode码点,
接着根据这个码点按照execution_character_set编码又是怎样去计算字节序列的,这个过程你再详细介绍下
你想彻底搞清楚两个核心问题:一是源文件中的常量字符串字节序列,按source_character_set解码后得到的是否是Unicode码点 (而非GBK等源编码的"码点");二是这些Unicode码点如何按execution_character_set编码,生成最终存入程序的字节序列------简单说,你想知道这两个转换步骤的具体计算过程和底层逻辑,下面我用"具象化的字符例子+分步拆解"讲清楚,全程围绕"输"这个字符(最易跟踪)展开,再扩展到完整字符串。
核心前提:编译器处理字符的"统一中转规则"
先明确一个关键:所有现代编译器(包括MSVC)处理字符常量时,都会先将"源编码的字节"转换为"Unicode码点"(字符的唯一数字标识),再从Unicode码点转换为"执行编码的字节"。
- Unicode码点:是全球所有字符的"唯一身份证"(比如"输"的码点是
U+8F93,"入"是U+5165),和具体编码无关; - GBK/UTF-8:是将Unicode码点"翻译"成不同字节序列的"规则"(GBK是查表映射,UTF-8是算法编码),本身没有"码点"概念,只有"字节序列"。
第一步:source_character_set 解码 → 得到 Unicode 码点(核心:统一转Unicode)
这个步骤的目标是:把"源文件里存储的字符串字节",转换成编译器能统一处理的Unicode码点(而非源编码的"码点"------因为GBK这类编码根本没有独立的"码点体系",只有字节序列)。
关键结论:解码后得到的一定是 Unicode 码点,而非GBK/UTF-8的"码点"
GBK的本质是"字符→字节"的直接映射方案,它没有像Unicode那样的"码点"概念------GBK的0xC8EB不是"码点",只是"输"这个字符的GBK编码字节序列;而Unicode的U+8F93是"输"的唯一数字标识,是所有编码转换的"中间桥梁"。
分步拆解(用"输"字举例)
| 源文件编码(source_character_set) | 源文件中"输"的字节序列 | 解码过程(编译器做的事) | 最终得到的Unicode码点 |
|---|---|---|---|
| GBK | 0xC8 0xEB(2字节) |
查GBK→Unicode映射表: GBK字节0xC8EB ↔ Unicode U+8F93 |
U+8F93 |
| UTF-8(带BOM) | 0xE8 0xBE 0x93(3字节) |
按UTF-8解码规则计算: UTF-8字节E8BE93 → 二进制解析后得到 0x8F93 → 即U+8F93 |
U+8F93 |
补充:UTF-8解码的具体计算(新手可略过,理解"规则转换"即可)
UTF-8是变长编码,有明确的二进制解码规则:
E8的二进制:11101000→ 表示这是3字节UTF-8,取后3位1000;BE的二进制:10111110→ 取后6位111110;93的二进制:10010011→ 取后6位010011;- 拼接:
1000+111110+010011=1000111110010011→ 十进制36755→ 十六进制0x8F93→ 即U+8F93。
第二步:Unicode码点 → 按 execution_character_set 编码 → 生成程序中的字节序列
这个步骤的目标是:把统一的Unicode码点,转换成最终存入程序二进制文件的字节序列(程序运行时,cout输出的就是这些字节)。
核心逻辑:按执行字符集的规则,将Unicode码点"翻译"成字节序列
- 若执行字符集是GBK:查表映射(Unicode→GBK);
- 若执行字符集是UTF-8:算法编码(按UTF-8规则计算);
- 所有转换的输入都是"Unicode码点",和源文件编码无关。
分步拆解(仍用"输"的U+8F93举例)
| 执行字符集(execution_character_set) | 输入的Unicode码点 | 编码过程(编译器做的事) | 最终存入程序的字节序列 |
|---|---|---|---|
| GBK(VS默认) | U+8F93 |
查Unicode→GBK映射表: U+8F93 ↔ GBK字节0xC8EB |
0xC8 0xEB |
| UTF-8 | U+8F93 |
按UTF-8编码规则计算: U+8F93属于0x0800~0xFFFF范围,对应3字节UTF-8格式: 1110xxxx 10xxxxxx 10xxxxxx → 填充后得到E8BE93 |
0xE8 0xBE 0x93 |
补充:UTF-8编码的具体计算(新手可略过)
UTF-8编码规则(针对U+8F93,即0x8F93):
- 确定长度:
0x8F93在0x0800 ~ 0xFFFF之间 → 3字节UTF-8; - 拆分二进制:
0x8F93=1000 1111 1001 0011; - 填充到3字节模板:
- 第1字节:
1110+ 前4位1000→11101000(0xE8); - 第2字节:
10+ 中间6位111110→10111110(0xBE); - 第3字节:
10+ 最后6位010011→10010011(0x93);
- 第1字节:
- 最终字节:
E8 BE 93。
完整流程示例:"输入"字符串的全转换过程
为了让你更直观,用"输入"两个字串联全流程(源文件UTF-8,执行字符集GBK):
- 源文件(UTF-8)中"输入"的字节:
E8BE93 E585A5; - 按source_character_set=UTF-8解码:
E8BE93→U+8F93(输);E585A5→U+5165(入);
- 按execution_character_set=GBK编码:
U+8F93→C8EB(输);U+5165→C8F7(入);
- 程序中存储的字节:
C8EB C8F7; - 运行时
cout输出这些字节,控制台(GBK)解码→正确显示"输入"。
总结
- 解码阶段(source_character_set):无论源文件是GBK/UTF-8,最终都转换为Unicode码点(而非源编码的"码点",因为GBK无码点概念);
- 编码阶段(execution_character_set):以Unicode码点为输入,按执行字符集规则(查表/算法)生成字节序列,存入程序;
- 核心逻辑:Unicode是"中间翻译官",让源编码和执行编码的差异被统一消解,只要两端转换规则正确,就不会乱码。