从C风格字符串到现代C++字符串的演进
前言字符串大家在编程中用的应该是比较多的。大家常用的是C风格的还是现代C的std::string封装从C到C字符串经历了很大了演进与优化。今天我们来看看字符串的升级过程。目录一、C风格的字符串二、C标准库字符串一、C风格的字符串C语言没有内置的字符串类型它的字符串通常表示为一个以\0结尾的字符数组。#include iostream #include cstring // strlen, strcpy, strcat, strcmp int main() { // 1. 声明与初始化 char s1[] hello; // 数组sizeof6含\0 char s2[20] world; // 预留空间 const char* s3 literal; // 指针指向字符串常量只读 std::cout s1 length: strlen(s1) \n; // 5不计\0 // 2. 拷贝危险目标必须够大 char buffer[64]; strcpy(buffer, s1); // 若 buffer 太小 → 缓冲区溢出 std::cout copied: buffer \n; // 3. 安全拷贝推荐 strncpy(buffer, s1, sizeof(buffer) - 1); buffer[sizeof(buffer) - 1] \0; // strncpy 不保证加 \0 return 0; }这种传统的C风格字符数组虽然看着非常简单但它缺乏类型安全和便捷的接口主要有以下几点缺陷1) 复杂的内存管理程序员必须自己手动管理字符数组的大小和终止符(\0)。如果忘记了给终止符预留空间容易导致内存访问越界。2) 缺乏边界检查标准库函数如strcpy、strcat等不检查目标数组的大小容易导致缓冲区溢出。3) 无法自动动态扩容C风格字符串一版存储在固定大小的数组中。如果需要拼接或扩展程序员必须预先分配足够大的空间或者手动实现动态扩容等逻辑。这增加了编程复杂度容易因预估不足导致出现截断或缓冲区溢出等问题二、C标准库字符串相比C风格的字符串C演进的std::string比较安全与方便。C标准库的std::string类本质是一个容器类它封装了字符数组并提供了丰富的成员函数来操作字符串。从C98到C23std::string经历了多次改进引入很多新特性和优化策略。这里我们主要聊一聊字符串在C98、C11、C17这三个阶段的一些主要变化。1) C98 std::stringC98首次引入了std::string类该类内部包含了一个指向堆内存的指针、字符串长度和容量等信息。#include string int main() { std::string a hello; std::string b world; // 拷贝自动管理内存 std::string c a; // 拼接安全自动扩容 c b; return 0; }有一点需要注意如果是空字符串也可能需要进行一次堆内存分配。为了减少小字符串带来的频繁堆分配开销C引入了小字符串优化Small String Optimization, SSO。SSO允许在字符串对象内部存储较短的字符串内容而不必分配堆内存。2) C11 移动语义的革命C引入了移动语义这是字符串优化的一次重大飞跃。移动语义允许资源如堆内存在对象之间“转移”而非复制从而避免不必要的深拷贝。当我们把临时字符串右值赋值或初始化另一个字符串时编译器会优先调用移动构造函数或移动赋值运算符直接将源字符串的内部缓冲区指针“偷”过来例如std::string a Hello World!; std::string b std::move(a); // 移动语义a的资源被转移到b3) C17 std::string_viewC17引入了std::string_view它是一个轻量级的字符串视图类它不拥有字符串数据的内存而只是引用一段已存在的字符序列不涉及堆内存的分配。一个std::string_view一般包含一个指针和一个长度它大小毕竟固定而且很小创建开销极低。std::string_view的一些见到那代码示例如下std::string str hello world; std::string_view sv(str); // 零拷贝仅持有指针长度 sv.remove_prefix(6); // 跳过前6个字符sv 变为 world sv.remove_suffix(2); // 截掉后2个字符sv 变为 wor // 作为函数参数避免 std::string 的深拷贝 void process(std::string_view sv) { // 可接受 string、const char*、string_view std::cout sv; } process(str); // 无拷贝 process(literal); // 无拷贝写在最后C20及之后的版本对于字符串处理方面有更安全、更高效的优化方式感兴趣的童鞋可以继续深入做一些了解。