C/C++ 内存与字符串操作函数详解
一、C 语言标准库函数(<string.h>/ C++ 中<cstring>)
所有函数均为字节级裸指针操作,字符串默认以'\0'作为结束标志。
1. 内存操作函数(mem- 前缀 = memory 内存)
这类函数不关心内存中的数据类型,按字节数操作连续内存块。
memcpy(memory copy,内存拷贝)
原型:void *memcpy(void *dest, const void *src, size_t n);
从 src 地址拷贝 n 个字节到 dest 地址。不处理内存重叠,若源和目标区域重叠,行为未定义,重叠场景必须使用 memmove。memmove(memory move,内存移动)
原型:void *memmove(void *dest, const void *src, size_t n);
功能同 memcpy,但支持内存区域重叠,内部通过临时缓存保证拷贝正确性,是实现字符串中间删除的核心函数。memset(memory set,内存填充)
原型:void *memset(void *s, int c, size_t n);
将 s 指向的前 n 个字节全部赋值为字符 c。常用于内存清零、字符串清空,仅对 char 类型赋值任意值安全,不可用于给 int、结构体等非字节类型赋值非 0 值。memcmp(memory compare,内存比较)
原型:int memcmp(const void *s1, const void *s2, size_t n);
按字节逐位比较前 n 个字节,返回值小于 0 表示 s1 更小,等于 0 表示完全相同,大于 0 表示 s1 更大。memchr(memory character,内存字符查找)
原型:void *memchr(const void *s, int c, size_t n);
在前 n 个字节中查找字符 c 第一次出现的地址,找到返回对应指针,未找到返回 NULL。
2. 字符串操作函数(str- 前缀 = string 字符串)
所有字符串函数都依赖结尾的'\0'判断边界,操作前必须保证字符串合法结束。
(1)拷贝与赋值
strcpy(string copy,字符串拷贝)
原型:char *strcpy(char *dest, const char *src);
将 src 完整拷贝到 dest,包含结尾的'\0'。不检查 dest 容量,存在缓冲区溢出风险。strncpy(string n copy,受限长度字符串拷贝)
原型:char *strncpy(char *dest, const char *src, size_t n);
最多拷贝 n 个字符。注意:若 src 长度 ≥ n,拷贝后不会自动追加'\0',需手动补全;若 src 更短,剩余位置自动用'\0'填充。strdup(string duplicate,字符串复制)
原型:char *strdup(const char *s);
内部调用 malloc 分配堆内存并拷贝字符串,返回新字符串的首地址。使用后必须手动 free 释放,否则会造成内存泄漏。
(2)拼接追加
strcat(string concatenate,字符串拼接)
原型:char *strcat(char *dest, const char *src);
将 src 拼接到 dest 尾部,覆盖原结尾的'\0'并保留新的结束符。同样存在缓冲区溢出风险。strncat(string n concatenate,受限长度字符串拼接)
原型:char *strncat(char *dest, const char *src, size_t n);
最多拼接 n 个字符,且会自动在末尾追加'\0',比 strcat 更安全。
(3)字符串比较
strcmp(string compare,字符串比较)
原型:int strcmp(const char *s1, const char *s2);
按 ASCII 字典序逐字符比较,返回值正负零分别表示 s1 小于、等于、大于 s2。strncmp(string n compare,受限长度字符串比较)
原型:int strncmp(const char *s1, const char *s2, size_t n);
仅比较前 n 个字符,超出长度的部分不参与比较。
(4)查找与定位
strchr(string character,正向字符查找)
原型:char *strchr(const char *s, int c);
正向查找字符 c 在字符串中第一次出现的地址,未找到返回 NULL。strrchr(string reverse character,反向字符查找)
原型:char *strrchr(const char *s, int c);
反向查找字符 c 最后一次出现的地址,r 代表 reverse(反向)。strstr(string string,子串查找)
原型:char *strstr(const char *haystack, const char *needle);
在主串 haystack 中查找子串 needle 第一次出现的起始地址,未找到返回 NULL。strspn(string span,前缀匹配长度)
原型:size_t strspn(const char *s, const char *accept);
返回字符串 s 开头连续包含 accept 字符集的字符个数,span 表示 “跨度、范围”。strcspn(string complement span,前缀不匹配长度)
原型:size_t strcspn(const char *s, const char *reject);
返回字符串 s 开头连续不包含 reject 字符集的字符个数,c 代表 complement(补集)。
(5)长度与分割
strlen(string length,字符串长度)
原型:size_t strlen(const char *s);
返回字符串的字符个数,不包含结尾的'\0'。strtok(string token,字符串分割)
原型:char *strtok(char *str, const char *delim);
按分隔符 delim 分割字符串,首次调用传原串,后续传 NULL 继续分割。会修改原字符串,且不可重入、线程不安全;可重入版本为strtok_r(string token reentrant,可重入字符串分割),安全版本为strtok_s(string token safe,安全字符串分割)。
(6)清空与删除(核心补充)
C 标准库没有提供名为 strdelete 的专属删除函数,字符串删除、清空均通过已有内存 / 字符串函数间接实现:
整串清空
用
strcpy(dest, ""):写入空字符串,仅修改首字节为'\0',效率最高。用
memset(dest, 0, len):将整片内存清零,适合需要彻底清空数据的场景。
删除指定位置的字符 / 子串
核心使用memmove实现:将删除位置后方的字符整体前移,覆盖被删除的内容,自动保留结尾的'\0'。
示例:删除字符串 s 中第 pos 位开始的 len 个字符memmove(s+pos,s+pos+len,strlen(s+pos+len)+1);必须使用 memmove 而非 memcpy,因为前移时源和目标内存区域重叠。
二、C++ 内存与字符串操作
C++ 完全兼容 C 语言的所有库函数,同时提供了面向对象的字符串类和更安全的内存管理机制。
1. 内存操作
(1)原生内存管理操作符
new / new[]:分配堆内存 + 自动调用对象构造函数,new 对应单个对象,new [] 对应对象数组。
delete / delete[]:调用对象析构函数 + 释放堆内存,必须与 new /new [] 配对使用,不可混用。
(2)标准库算法(<algorithm>)
替代 C 语言的 mem 系列函数,适用于非平凡对象(有构造 / 析构函数的类对象),保证类型安全:
std::copy(standard copy,区间拷贝):将指定区间的元素拷贝到目标位置,对应 C 语言的 memcpy,但支持对象的拷贝构造。
std::fill(standard fill,区间填充):将指定区间的所有元素赋值为指定值,对应 C 语言的 memset,但类型安全,可用于任意类型。
(3)底层内存工具(<memory>)
- std::uninitialized_copy:在未初始化的内存上构造并拷贝对象,多用于容器底层实现,日常开发较少直接使用。
2. 字符串操作:std::string(<string>)
std::string自动管理内存、动态扩容,无需手动处理'\0',提供了完整的增删改查接口。
(1)构造与赋值
默认构造:
std::string s;生成空字符串C 串构造:
std::string s("hello");用 C 风格字符串初始化重复字符构造:
std::string s(5, 'a');生成 5 个 ‘a’ 的字符串拷贝构造:
std::string s2(s1);复制另一个字符串assign(赋值)成员函数:支持按位置、长度截取赋值,用法灵活。
(2)拼接与追加
+=运算符重载:直接追加字符串、字符、C 风格字符串append(追加)成员函数:支持多种重载,可追加子串、重复字符等
push_back(尾部压入):追加单个字符
(3)长度与容量
size() / length():返回字符串字符个数,二者功能完全等价
empty():判断字符串是否为空
capacity():返回当前已分配的内存容量
reserve():预分配内存容量,减少扩容开销
resize():调整字符串长度,超出截断,不足则填充指定字符
(4)字符访问
operator[]:下标访问,越界行为未定义,性能更高at():下标访问,越界时抛出
std::out_of_range异常,更安全front() / back():访问首尾字符(C++11)
c_str():返回以
'\0'结尾的const char*指针,用于兼容 C 语言接口
(5)查找、修改与删除(补充完整删除接口)
所有查找函数返回size_t类型的下标,未找到时返回std::string::npos。
查找类
find():正向查找子串或字符第一次出现的位置
rfind():反向查找子串或字符最后一次出现的位置
find_first_of():查找字符集中任意字符第一次出现的位置
find_last_of():查找字符集中任意字符最后一次出现的位置
修改与删除类
substr(sub string,截取子串):返回指定位置、长度的子串,不修改原字符串
replace(替换):替换指定区间的内容为新字符串
insert(插入):在指定位置插入字符或字符串
erase(擦除 / 删除):专属删除函数,支持三种重载:
删除指定位置开始的指定长度:
s.erase(pos, len);删除迭代器指向的单个字符:
s.erase(iter);删除迭代器区间内的所有字符:
s.erase(begin_iter, end_iter);
pop_back(尾部弹出):删除最后一个字符(C++11)
clear(清空):删除所有字符,将字符串置为空,等价于
s.erase()
(6)C++17 扩展:std::string_view
只读字符串视图,不持有内存,仅记录指针和长度,用于高效传递字符串,避免无意义的内存拷贝。
关键注意事项
安全优先:C 语言的 strcpy、strcat 无边界检查,生产环境优先使用带长度限制的 strncpy、strncat,或直接使用 C++ 的 std::string。
strncpy 陷阱:源串长度 ≥ 限制长度时,不会自动补
'\0',必须手动设置结束符。重叠内存操作:内存区域重叠时,拷贝必须使用 memmove,不能用 memcpy。
指针失效:std::string 调用 c_str () 返回的指针,在字符串修改、扩容、析构后会立即失效,不可继续使用。
npos 类型:std::string::npos 是无符号 size_t 类型,不要用有符号 int 变量与其比较,避免溢出导致逻辑错误。