C/C++字符串
C语言字符串
C 语言中的 “string” 并不是一种内建类型,而是一种以 '\0'(null 字符)结尾的字符数组,用来表示字符串。C 语言的字符串处理完全是基于字符数组和标准库函数的组合实现的,理解起来要靠对内存、指针和字符操作的深入掌握。
一、C语言字符串的本质
定义
C 语言的字符串是 一串字符 + 结尾的 '\0'(null 终止符),用于标记字符串的结束。
char str[] = "hello";
这其实是一个字符数组,相当于:
char str[6] = { 'h', 'e', 'l', 'l', 'o', '\0' };
✅ 存储结构图
| 字符 | h | e | l | l | o | \0 |
|---|---|---|---|---|---|---|
| 下标 | 0 | 1 | 2 | 3 | 4 | 5 |
二、定义字符串的几种方式
1. 使用字符数组
char s1[] = "hello"; // 推荐方式,会自动添加 '\0'
2. 使用指针指向字符串常量(只读)
const char* s2 = "world"; // 指向只读区域,不能修改 *s2
3. 自定义字符数组初始化
char s3[10] = {'h','e','l','l','o','\0'};
三、常用字符串处理函数(来自 <string.h>)
| 函数 | 功能 | 示例 |
|---|---|---|
strlen(s) |
获取长度(不含\0) |
strlen("abc") == 3 |
strcpy(dest, src) |
拷贝字符串 | strcpy(a, b); |
strncpy(dest, src, n) |
拷贝 n 字符 | strncpy(a, b, 5); |
strcat(dest, src) |
拼接字符串 | strcat(a, b); |
strcmp(a, b) |
比较字符串 | strcmp("a","b")<0 |
strncmp(a,b,n) |
比较前 n 个字符 | strncmp("abc","abd",2)==0 |
strchr(s, c) |
查找字符 | 返回首次出现的位置 |
strstr(s, sub) |
查找子串 | 返回子串首次位置 |
四、常见问题与坑
1. 没有 \0 会导致未知行为
char s[5] = {'h','e','l','l','o'}; // 没有终止符!
printf("%s\n", s); // 可能会无限打印直到遇到内存中偶然的 '\0'
2. 越界写入
char s[5] = "hell";
strcat(s, "o"); // 没有足够空间会越界!
3. 修改字符串常量(未定义行为)
char* s = "hello";
s[0] = 'H'; // ❌ 未定义行为:字符串字面量可能在只读区
应写为:
char s[] = "hello"; // 合法可修改
五、字符串传参
字符串在函数中传递时通常是 传指针,即:
void print(const char* s) {
printf("%s\n", s);
}
int main() {
char msg[] = "Hello";
print(msg);
}
六、字符串内存分区
| 存储位置 | 类型 | 示例 |
|---|---|---|
| 静态区(只读) | 字符串字面量 | char* s = "hello"; |
| 栈区 | 字符数组 | char s[10] = "abc"; |
| 堆区 | 动态分配 | char* s = malloc(n); |
注意:
char* s = "hello"; // 指向静态只读区
char s2[] = "hello"; // 拷贝到栈区,可修改
C++字符串
C++ 中的字符串主要是通过 std::string(标准库提供的类)来实现的,它相对于 C 语言的字符串更 安全、灵活、易用。本回答将从基础介绍、常用操作、内存管理、与 C 字符串的转换等方面详细讲解。
一、std::string 是什么?
std::string 是 C++ 标准模板库(STL)中的一个类,定义在头文件 <string> 中。它封装了动态字符数组的管理,并提供了丰富的成员函数来操作字符串。
#include <string>
std::string s = "hello";
本质上,std::string 是:
- 一个自动管理内存的动态数组(可增长)
- 使用深拷贝语义
- 具备操作符重载(如
+,==)
二、常见构造方式
string(const char* s);//使用字符串s初始化string(const string& str);//使用一个string对象初始化另一个string对象string(int n, char c);//使用n个字符c初始化
std::string s1; // 空字符串
std::string s2("hello"); // 从 C 字符串构造
std::string s3 = "world"; // 赋值初始化
std::string s4(s2); // 拷贝构造
std::string s5(5, 'A'); // 结果为 "AAAAA"
三、常用 API 和操作
赋值
赋值的函数原型:
string& operator=(const char* s);//char*类型字符串 赋值给当前的字符串string& operator=(const string &s);//把字符串s赋给当前的字符串string& operator=(char c);//字符赋值给当前的字符串string& assign(const char *s);//把字符串s赋给当前的字符串string& assign(const char *s, int n);//把字符串s的前n个字符赋给当前的字符串string& assign(const string &s);//把字符串s赋给当前字符串string& assign(int n, char c);//用n个字符c赋给当前字符串
添加、拼接
string& operator+=(const char* str);//重载+=操作符string& operator+=(const char c);//重载+=操作符string& operator+=(const string& str);//重载+=操作符string& append(const char *s);//把字符串s连接到当前字符串结尾string& append(const char *s, int n);//把字符串s的前n个字符连接到当前字符串结尾string& append(const string &s);//同operator+=(const string& str)string& append(const string &s, int pos, int n);//字符串s中从pos开始的n个字符连接到字符串结尾
std::string s = "Hello";
s += " World"; // 拼接
s.append("!!!"); // 追加字符串
获取长度
s.length(); // 返回长度
s.size(); // 同 length()
访问字符
char& operator[](int n);//通过[]方式取字符char& at(int n);//通过at方法获取字符
s[0]; // 无边界检查
s.at(1); // 带边界检查,越界抛异常
子串提取
s.substr(0, 5); // 提取前5个字符
查找和替换
int find(const string& str, int pos = 0) const;//查找str第一次出现位置,从pos开始查找int find(const char* s, int pos = 0) const;//查找s第一次出现位置,从pos开始查找int find(const char* s, int pos, int n) const;//从pos位置查找s的前n个字符第一次位置int find(const char c, int pos = 0) const;//查找字符c第一次出现位置int rfind(const string& str, int pos = npos) const;//查找str最后一次位置,从pos开始查找int rfind(const char* s, int pos = npos) const;//查找s最后一次出现位置,从pos开始查找int rfind(const char* s, int pos, int n) const;//从pos查找s的前n个字符最后一次位置int rfind(const char c, int pos = 0) const;//查找字符c最后一次出现位置string& replace(int pos, int n, const string& str);//替换从pos开始n个字符为字符串strstring& replace(int pos, int n,const char* s);//替换从pos开始的n个字符为字符串s
s.find("World"); // 找到返回位置,否则返回 string::npos
s.replace(6, 5, "C++"); // 替换指定位置的字符
比较
std::string a = "abc", b = "xyz";
a == b, a < b, a != b // 支持运算符重载
插入与删除
string& insert(int pos, const char* s);//插入字符串string& insert(int pos, const string& str);//插入字符串string& insert(int pos, int n, char c);//在指定位置插入n个字符cstring& erase(int pos, int n = npos);//删除从Pos开始的n个字符
s.insert(5, "++"); // 在下标5处插入字符串
s.erase(5, 2); // 删除从下标5开始的2个字符
四、和 C 字符串的转换
C++ → C
std::string s = "hello";
const char* cstr = s.c_str(); // 返回 const char* 指针
⚠ 注意:c_str() 返回的是只读指针,且生命周期受 std::string 控制,不能写入!
C → C++
const char* cstr = "world";
std::string s = cstr;
区别
C 语言的字符串(char* 或 char[])和 C++ 的 std::string 是两种本质不同的字符串处理方式。下面从多个维度做对比,并提供示例帮助理解:
一、核心区别概览
| 项目 | C 语言字符串 (char*) |
C++ 字符串 (std::string) |
|---|---|---|
| 类型本质 | 字符数组 / 指针 | 类(封装了字符容器) |
| 存储终结 | 以 '\0' 结尾标识结束 |
内部记录长度,不依赖 \0 |
| 内存管理 | 需手动分配与释放 | 自动管理内存(RAII) |
| 操作方式 | 使用 <string.h> 函数,如 strcpy, strlen |
成员函数,如 .append(), .find() |
| 安全性 | 容易越界、内存泄漏 | 类型安全、越界检查更强 |
| 拓展性 | 靠开发者手动扩容 | 动态扩容,透明管理 |
二、使用对比示例
✅ C 语言字符串
#include <stdio.h>
#include <string.h>
int main() {
char s[20] = "Hello";
strcat(s, " World");
printf("%s\n", s); // 输出:Hello World
}
char s[20]:必须事先预估好空间strcat:若不小心越界,易出错
✅ C++ 字符串
#include <iostream>
#include <string>
int main() {
std::string s = "Hello";
s += " World";
std::cout << s << std::endl; // 输出:Hello World
}
- 自动扩容,无需担心越界
- 代码更简洁、更安全
三、常用操作对比
| 操作 | C 写法 | C++ 写法 |
|---|---|---|
| 获取长度 | strlen(s) |
s.size() / s.length() |
| 拼接 | strcat(s1, s2) |
s1 += s2 / s1.append(s2) |
| 拷贝 | strcpy(dest, src) |
std::string s = src; |
| 查找子串 | strstr(s, "abc") |
s.find("abc") |
| 截取子串 | strncpy(dest,src, len) |
s.substr(start, len) |
| 比较 | strcmp(a, b) |
a == b / a < b/ a > b |
四、安全性对比
C 风险点:
char a[5] = "hi";
strcat(a, "world"); // 越界风险!
C++ 安全自动管理:
std::string a = "hi";
a += "world"; // 安全扩容
五、内存管理差异
| 特性 | C 语言字符串 | C++ string |
|---|---|---|
| 需自己分配空间 | 是 | 否(构造时自动分配) |
| 需自己释放内存 | 是(malloc/free) |
否(析构自动释放) |
| 拷贝时默认浅拷贝 | 是(指针) | 否(深拷贝) |
未经作者同意请勿转载
本文来自博客园作者:aixueforever,原文链接:https://www.cnblogs.com/aslanvon/p/18928928

浙公网安备 33010602011771号