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个字符为字符串str
  • string& 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个字符c
  • string& 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 否(析构自动释放)
拷贝时默认浅拷贝 是(指针) 否(深拷贝)
posted @ 2025-06-14 23:02  aixueforever  阅读(81)  评论(0)    收藏  举报