在C++的标准演进过程中,为了适配不同场景下的Unicode编码需求,先后引入了char16_t、char32_t以及char8_t三种字符类型,分别对应UTF-16、UTF-32和UTF-8编码方案,开发者可以根据实际场景选择合适的类型处理Unicode文本。

三种Unicode字符类型的基本定义
C++标准中定义的三种Unicode字符类型有各自对应的编码规则和存储大小,具体差异如下:
| 类型名称 | 对应编码 | 存储大小 | 适用场景 |
|---|---|---|---|
| char8_t | UTF-8 | 至少8位 | C++20起支持,处理UTF-8编码的文本,兼容ASCII |
| char16_t | UTF-16 | 至少16位 | C++11起支持,处理UTF-16编码的文本,常见于Windows系统API |
| char32_t | UTF-32 | 至少32位 | C++11起支持,处理UTF-32编码的文本,每个字符固定占4字节 |
Unicode字符串的声明方式
针对不同的Unicode字符类型,C++提供了对应的字符串字面量前缀,用于明确字符串的编码类型:
- UTF-8字符串:使用
u8前缀,对应char8_t类型(C++20起) - UTF-16字符串:使用
u前缀,对应char16_t类型 - UTF-32字符串:使用
U前缀,对应char32_t类型
以下是不同编码字符串的声明示例:
#include <iostream>
#include <cstdint>
int main() {
// C++20及以上支持char8_t和u8前缀的char8_t字符串
const char8_t* utf8_str = u8"你好,世界";
// UTF-16字符串,类型为const char16_t*
const char16_t* utf16_str = u"你好,世界";
// UTF-32字符串,类型为const char32_t*
const char32_t* utf32_str = U"你好,世界";
// 输出各字符串的字节大小,注意char8_t的大小是1字节,char16_t是2字节,char32_t是4字节
std::cout << "char8_t size: " << sizeof(char8_t) << std::endl;
std::cout << "char16_t size: " << sizeof(char16_t) << std::endl;
std::cout << "char32_t size: " << sizeof(char32_t) << std::endl;
return 0;
}
不同Unicode类型之间的转换
实际开发中经常需要在不同的Unicode编码之间进行转换,C++标准库提供了<codecvt>头文件中的相关工具,不过需要注意该头文件在C++17中被标记为废弃,后续标准可能会调整实现方式,以下示例基于C++11到C++14的标准实现:
#include <iostream>
#include <string>
#include <codecvt>
#include <locale>
// UTF-16转UTF-8的函数示例
std::string utf16_to_utf8(const std::u16string& utf16_str) {
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter;
return converter.to_bytes(utf16_str);
}
// UTF-8转UTF-16的函数示例
std::u16string utf8_to_utf16(const std::string& utf8_str) {
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter;
return converter.from_bytes(utf8_str);
}
int main() {
// 原始UTF-16字符串
std::u16string u16_str = u"测试文本";
// 转换为UTF-8
std::string u8_str = utf16_to_utf8(u16_str);
std::cout << "转换后的UTF-8字符串长度: " << u8_str.length() << std::endl;
// 再转回UTF-16
std::u16string u16_str2 = utf8_to_utf16(u8_str);
std::cout << "转回的UTF-16字符串长度: " << u16_str2.length() << std::endl;
return 0;
}
使用注意事项
在使用这三种Unicode字符类型时,需要注意以下几点:
- char8_t是C++20才正式引入的类型,如果使用低于C++20的编译器,无法直接使用char8_t,此时可以用
unsigned char或者char配合u8前缀处理UTF-8字符串,但无法明确类型语义。 - UTF-16编码是变长编码,部分生僻字符会占用两个16位单元(也就是4字节),处理时不能简单按照固定长度遍历字符。
- 标准输出流默认不支持直接输出char16_t和char32_t类型的字符串,需要转换为本地编码或者UTF-8编码后再输出,否则会出现乱码。
- 如果需要处理跨平台的Unicode文本,优先选择UTF-8编码,兼容性更好,大部分现代系统和库都默认支持UTF-8。
常见错误示例
很多开发者会误用普通char类型存储UTF-8字符串,虽然语法上允许,但无法明确编码语义,容易出现编码混淆的问题,以下是错误和正确用法的对比:
// 错误用法:用普通char存储UTF-8字符串,无法明确编码类型 const char* wrong_str = u8"你好"; // 正确用法(C++20及以上):用char8_t明确存储UTF-8字符串 const char8_t* right_str = u8"你好";
另外不要尝试直接将char16_t字符串赋值给char类型指针,两者的编码和存储大小完全不同,会导致数据截断或者乱码。