strcat 是 "string concatenate"(字符串连接)的缩写,它的作用是将源字符串(source string)追加到目标字符串(destination string)的末尾,并在连接后的新字符串末尾添加一个空字符 \0。
标准库 strcat 的行为回顾
在开始实现之前,我们先回顾一下标准库 strcat 的关键点:
- 函数原型:
char *strcat(char *dest, const char *src);
- 参数:
dest: 目标字符串的缓冲区。这个缓冲区必须足够大,能够容纳dest和src两个字符串的内容,以及一个额外的\0字符,如果空间不足,会导致缓冲区溢出,这是非常危险的。src: 源字符串,这个字符串通常是常量(const),意味着strcat函数不会修改它。
- 返回值:
- 返回一个指向
dest缓冲区首地址的指针。
- 返回一个指向
- 工作原理:
strcat会找到dest字符串的末尾(即第一个\0的位置)。- 它从
src字符串的第一个字符开始,逐个字符地拷贝到dest字符串的末尾。 - 它会在拷贝完
src的所有字符后,在dest的末尾添加一个\0。
strcat 的 C 语言实现
下面我们将分步实现一个自己的 my_strcat 函数。
实现思路
- 找到目标字符串的末尾:创建一个指针,让它指向
dest的起始位置,然后不断向后移动,直到遇到\0字符。 - 进行拷贝:这个指针就指向了
dest的末尾,我们再用一个指针指向src的起始位置,然后将src的内容逐个字符地拷贝到dest的末尾位置。 - 添加终止符:当
src的所有字符(包括它末尾的\0)都被拷贝过去后,dest就自动拥有了正确的终止符,返回dest的起始地址。
代码实现
#include <stdio.h>
/**
* @brief 自定义字符串连接函数
* @param dest 目标字符串缓冲区,必须足够大
* @param src 源字符串
* @return 返回连接后的目标字符串的起始地址
*/
char *my_strcat(char *dest, const char *src) {
char *ptr = dest; // 保存 dest 的起始地址,用于最后返回
// 1. 移动指针到 dest 字符串的末尾
// 循环在 *dest 不为 '\0' 时继续
while (*dest != '\0') {
dest++;
}
// 循环结束后,dest 指针指向了 dest 字符串末尾的 '\0' 的位置
// 2. 将 src 字符串的内容拷贝到 dest 的末尾
// 循环在 *src 不为 '\0' 时继续
while (*src != '\0') {
*dest = *src; // 拷贝字符
dest++; // 移动 dest 指针到下一个位置
src++; // 移动 src 指针到下一个位置
}
// 3. 在连接后的字符串末尾添加 '\0'
// 上面的循环在 *src 为 '\0' 时停止,*dest 已经被赋值为 '\0'
// 所以这一步是隐含的,但显式写上更清晰
*dest = '\0';
// 4. 返回连接后的字符串起始地址
return ptr;
}
// --- 测试代码 ---
int main() {
// 定义一个足够大的缓冲区
char str1[50] = "Hello, "; // 注意:要留出足够的空间
char str2[] = "world!";
printf("连接前 str1: %s\n", str1);
printf("连接前 str2: %s\n", str2);
// 调用自定义的 my_strcat 函数
// 将 str2 连接到 str1 的末尾
my_strcat(str1, str2);
printf("连接后 str1: %s\n", str1);
// 另一个测试用例
char path[100] = "/usr/local/";
char file[] = "bin/program";
my_strcat(path, file);
printf("构建的完整路径: %s\n", path);
return 0;
}
代码解析
char *ptr = dest;:我们创建了一个临时指针ptr来保存dest的原始地址,因为后续的dest指针会移动到字符串末尾,所以我们需要ptr来记住起点,以便最后返回。while (*dest != '\0') { dest++; }:这是第一个while循环,它的唯一目的就是找到dest字符串的结尾,当*dest(dest当前指向的字符)不是空字符\0时,指针dest就向后移动一位。while (*src != '\0') { ... }:这是第二个while循环,负责实际的拷贝工作。dest指针正停在dest原字符串末尾的\0位置,循环将src指向的字符逐个赋值给dest指向的位置,然后两个指针同时向后移动。*dest = '\0';:当src字符串被完全拷贝后,src指针会指向src字符串末尾的\0,循环结束,*dest的值就是*src的值,也就是\0,所以这行代码虽然是显式添加,但在逻辑上已经完成了。return ptr;:返回最初保存的dest的起始地址,这是标准strcat的行为。
安全性考虑与 strncat
标准 strcat 的主要问题是它不检查目标缓冲区的大小,非常容易导致缓冲区溢出,为了解决这个问题,C 标准库提供了 strncat。
strncat 增加了一个参数 n,用来限制最多可以拷贝的字符数。
strncat 的原型
char *strncat(char *dest, const char *src, size_t n);
strncat 的实现逻辑
strncat 的实现与 strcat 非常相似,只是在第二个拷贝循环中增加了一个计数器 n。
char *my_strncat(char *dest, const char *src, size_t n) {
char *ptr = dest;
// 1. 同样,先找到 dest 的末尾
while (*dest != '\0') {
dest++;
}
// 2. 拷贝,但最多只拷贝 n 个字符
// src 比 n 短,则在 src 结束时停止
// src 比 n 长,则在拷贝 n 个字符后停止
while (n > 0 && *src != '\0') {
*dest = *src;
dest++;
src++;
n--; // 每次拷贝后,n 减 1
}
// 3. 确保结果字符串总是以 '\0'
*dest = '\0';
return ptr;
}
strncat 比 strcat 更安全,因为它可以防止拷贝超过目标缓冲区容量的数据,但在使用 strncat 时,仍需确保目标缓冲区至少有 strlen(dest) + n + 1 的空间。
| 特性 | strcat |
my_strcat (自定义实现) |
strncat (安全版) |
|---|---|---|---|
| 功能 | 字符串连接 | 自定义字符串连接 | 安全的字符串连接 |
| 原型 | char *strcat(char *dest, const char *src) |
char *my_strcat(char *dest, const char *src) |
char *strncat(char *dest, const char *src, size_t n) |
| 安全性 | 不安全,易发生缓冲区溢出 | 不安全,与标准版一样 | 相对安全,通过 n 参数限制拷贝长度 |
| 核心逻辑 | 找到 dest 末尾拷贝 src 到末尾 |
找到 dest 末尾拷贝 src 到末尾 |
找到 dest 末尾最多拷贝 n 个字符 |
| 返回值 | dest 的起始地址 |
dest 的起始地址 |
dest 的起始地址 |
理解 strcat 的实现原理是 C 语言指针操作和字符串处理的一个非常好的练习,在实际开发中,强烈推荐使用 strncat 或更安全的、能自动计算长度的 strlcat(在 BSD 系统中可用),以避免缓冲区溢出的安全风险。
