在 C++ 中,处理字符串和字符有多种方式,每种方式都有其特定的用途和特点。本文将详细介绍 C++ 中的各种字符串和字符类型。
1. 字符类型
char
- 最基本的字符类型,占用 1 字节
- 可以存储 ASCII 字符
- 范围:-128 到 127 或 0 到 255(取决于是否有符号)
char c = 'A'; // 单个字符
wchar_t
- 宽字符类型,用于存储 Unicode 字符
- 通常占用 2 或 4 字节
- 用于处理多语言字符
wchar_t wc = L'中'; // 宽字符
2. 字符串字面量
- 存储位置
- 字符串字面量存储在程序的只读数据段
- 多个相同的字符串字面量可能共享同一块内存
- 字符串字面量总是以
\0结尾(这是 C/C++ 标准规定的)
const char* s1 = "Hello";
const char* s2 = "Hello";
// s1 和 s2 指向同一块内存
// 实际存储为:'H' 'e' 'l' 'l' 'o' '\0'
- 生命周期
- 字符串字面量的生命周期贯穿整个程序运行期间
- 不要尝试修改字符串字面量
const char* s = "Hello";
s[0] = 'h'; // 未定义行为!可能导致程序崩溃
- 类型转换
- 字符串字面量的类型是
const char[N](N 是字符串长度加1) - 可以隐式转换为
const char*
const char* s1 = "Hello"; // 正确
char* s2 = "Hello"; // 不推荐,可能导致未定义行为
char s3[] = "Hello"; // 正确,创建新的字符数组
// 字符串字面量的长度计算
const char* str = "Hello";
size_t len1 = strlen(str); // 结果是 5,不包含 \0
size_t len2 = sizeof("Hello"); // 结果是 6,包含 \0
- 常见问题
// 问题1:忘记结束符
char str[5] = "Hello"; // 错误:没有空间存储 '\0'
// 问题2:缓冲区溢出
char str[5] = "Hello";
strcpy(str, "Hello World"); // 错误:缓冲区溢出
// 问题3:内存泄漏
char* str = new char[6];
strcpy(str, "Hello");
// 忘记 delete[] str;
// 问题4:字符串字面量中的特殊字符
const char* s1 = "Hello\n"; // 包含换行符
const char* s2 = "Hello\\n"; // 包含反斜杠和n
const char* s3 = "Hello\0"; // 显式包含结束符(不推荐)
3. 字符串类型
C 风格字符串 (char* 和 const char*)
char*是可修改的指针const char*是只读的指针
char* str1 = "Hello"; // 不推荐,应该使用 const char*
const char* str2 = "Hello"; // 推荐
char str3[] = "Hello"; // 字符数组
std::string
- C++ 标准库提供的字符串类
- 动态分配内存
- 提供丰富的字符串操作方法
- 自动管理内存
#include <string>
std::string str = "Hello";
str += " World"; // 字符串拼接
str.length(); // 获取长度
str.substr(0, 5); // 获取子串
std::wstring
- 宽字符串类型,用于处理 Unicode 字符串
- 是
wchar_t的字符串版本 - 与
std::string有类似的接口
#include <string>
std::wstring wstr = L"你好,世界";
std::wcout << wstr << std::endl;
std::string_view (C++17)
- 字符串的非拥有视图
- 不复制字符串数据,只保存指向数据的指针和长度
- 适用于只读字符串操作
- 性能优于
std::string,因为避免了内存分配
#include <string_view>
// 从字符串字面量创建
std::string_view sv1 = "Hello";
// 从 std::string 创建
std::string str = "World";
std::string_view sv2 = str;
// 从子串创建
std::string_view sv3 = sv1.substr(0, 3); // "Hel"
// 常用操作
sv1.length(); // 获取长度
sv1.find("lo"); // 查找子串
sv1.substr(1, 3); // 获取子串
注意事项:
string_view不拥有数据,使用时需要确保原始数据有效- 不要返回局部变量的
string_view - 适合作为函数参数,特别是只读字符串参数
4. 各种类型之间的转换
// string 转 const char*
std::string str = "Hello";
const char* cstr = str.c_str();
// const char* 转 string
const char* cstr = "Hello";
std::string str(cstr);
// char 转 string
char c = 'A';
std::string str(1, c);
5. 注意事项
- 字符串字面量是只读的,不要尝试修改
- 注意字符串的编码问题
- 使用
std::string时要注意性能开销 - 处理大量字符串操作时考虑使用字符串视图
- 注意字符串比较和查找的大小写敏感性