C++ 字符串和字符类型详解

245 阅读3分钟

在 C++ 中,处理字符串和字符有多种方式,每种方式都有其特定的用途和特点。本文将详细介绍 C++ 中的各种字符串和字符类型。

1. 字符类型

char

  • 最基本的字符类型,占用 1 字节
  • 可以存储 ASCII 字符
  • 范围:-128 到 127 或 0 到 255(取决于是否有符号)
char c = 'A';  // 单个字符

wchar_t

  • 宽字符类型,用于存储 Unicode 字符
  • 通常占用 2 或 4 字节
  • 用于处理多语言字符
wchar_t wc = L'中';  // 宽字符

2. 字符串字面量

  1. 存储位置
  • 字符串字面量存储在程序的只读数据段
  • 多个相同的字符串字面量可能共享同一块内存
  • 字符串字面量总是以 \0 结尾(这是 C/C++ 标准规定的)
const char* s1 = "Hello";
const char* s2 = "Hello";
// s1 和 s2 指向同一块内存
// 实际存储为:'H' 'e' 'l' 'l' 'o' '\0'
  1. 生命周期
  • 字符串字面量的生命周期贯穿整个程序运行期间
  • 不要尝试修改字符串字面量
const char* s = "Hello";
s[0] = 'h';  // 未定义行为!可能导致程序崩溃
  1. 类型转换
  • 字符串字面量的类型是 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. 常见问题
// 问题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); // 获取子串

注意事项:

  1. string_view 不拥有数据,使用时需要确保原始数据有效
  2. 不要返回局部变量的 string_view
  3. 适合作为函数参数,特别是只读字符串参数

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. 注意事项

  1. 字符串字面量是只读的,不要尝试修改
  2. 注意字符串的编码问题
  3. 使用 std::string 时要注意性能开销
  4. 处理大量字符串操作时考虑使用字符串视图
  5. 注意字符串比较和查找的大小写敏感性