5. strncpy函数:
5.1 strncpy的使用:

c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main()
{
char arr[20] = "hello world";
char arr2[20] = { 0 };
strncpy(arr2, arr, 5);
printf("%s", arr2);
}
这段代码就可以控制字符串的的复制,需要注意的是:- 源字符串长度 < n
:
strncpy
会复制全部源字符(包括其自带的 \0
),并在目标缓冲区**剩余空间填充 \0
直到写满 n
个字符
示例 :复制 "Hi"
(长度2)到 n=8
的目标缓冲区,结果如 "Hi\0\0\0\0\0\0"
。
-
源字符串长度 ≥
n
:
仅复制前n
个字符, 不会自动添加\0
。若未手动添加终止符,目标字符串可能因缺少\0
导致后续操作(如printf
)出现未定义行为(如乱码或崩溃)5.2strncpy的模拟使用:
我们来尝试使用代码来复原:
c
char* my_strncpy(char *dest, const char *str, size_t num)
{
for (int i = 0; i < num; i++)
{
if (*(str + i) != 0)
{
*(dest + i) = *(str + i);
}
}
return dest;
}
int main()
{
char arr[20] = "hello world";
char arr2[20] = { 0 };
my_strncpy(arr2, arr, 12);
printf("%s", arr2);
}
这段函数就完成了对strncpy的模仿。
6. strncat函数:
6.1 strncat的使用:
strncat
和strcat
一样都是追加字符到原来的目标字符串;要求目标的字符串需要足够的空间,并返回目标字符串的开头的地址。我们来尝试以下如何使用:
c
int main()
{
char* a = "world";
char arr[40] = "学习c,hello ";
strncat(arr, a, 5);
printf("%s", arr);
}
这一小段代码就可以完成对arr这个字符串的追加,我们这次来深究以下arr字符串:
strncat()
的工作原理
- 定位目标字符串的结尾
- 函数首先找到目标字符串
dest
的结尾(即dest
的\0
位置)。 - 例如:若
dest = "Hello"
,则从'o'
后的\0
处开始追加。
- 函数首先找到目标字符串
- 追加字符并处理终止符
-
情况 1:
n
≤ 源字符串长度
复制源字符串src
的前n
个字符到dest
末尾,然后自动追加一个\0
。
示例 :cchar dest[10] = "Hi"; strncat(dest, "World", 3); // 结果:"HiWor\0"
- 复制
'W'
、'o'
、'r'
后,函数自动添加\0
。
- 复制
-
情况 2:
n
> 源字符串长度
复制整个src
(包括其自身的\0
),并用\0
填充剩余空间 直到总追加字符数达到n
。
示例 :cchar dest[10] = "A"; strncat(dest, "BC", 5); // 结果:"ABC\0\0\0"
- 源字符串
"BC"
长度=2(含\0
),追加后补充 3 个\0
至总长度 5。
- 源字符串
-
- 返回值
返回目标字符串dest
的指针,便于链式调用。
特性 | strncat() |
strncpy() |
---|---|---|
终止符处理 | 总是追加 \0 |
仅在 src 长度 < n 时填充 \0 |
填充行为 | 仅填充至总追加字符数为 n |
用 \0 填满整个 n 位 |
目标缓冲区修改起点 | dest 的结尾 \0 处 |
dest 的起始位置 |
安全性 | 更高(强制终止) | 需手动添加 \0 ,否则可能出错 |
6.2strncat的模拟使用:
c
char* my_strncat(char* dest,const char* str, size_t num)//通过指针来完成对地址的接收
{
char *tmp = dest;
while (*dest++);
*dest--;//当完成了*str == 0 时,str还会在加一次,我们减一次,完成对\0的赋值;
for (int i = 0; i < num; i++)
{
*(dest + i) = *(str + i);
}
*(dest + num) = '\0';
return tmp ;
}
int main()
{
char* a = "world";
char arr[40] = "学习c,hello ";
my_strncat(arr, a, 5);
printf("%s", arr);
}
我的第一版代码是有错误的;没有想到未处理源字符串提前结束,这会导致会越界访问,还有逻辑上的麻烦处理了,因此我们需要做出以下的改变:
c
#include <stdio.h>
#include <assert.h>
char* my_strncat(char* dest, const char* src, size_t num)
{
assert(dest && src); // 确保指针有效
char* ret = dest; // 保存目标字符串起始地址
// 1. 定位到目标字符串的结束符
while (*dest != '\0')
{
dest++;
}
// 2. 复制最多num个字符(遇'\0'则提前停止)
while (num-- && *src != '\0')
{
*dest++ = *src++;
}
// 3. 在末尾添加终止符(关键修正!)
*dest = '\0';
return ret;
}
int main()
{
const char* a = "world"; // 使用const避免警告
char arr[40] = "学习c,hello ";
// 测试1:正常追加
my_strncat(arr, a, 5);
printf("追加后: %s\n", arr); // 输出:学习c,hello world
// 测试2:源字符串短于num
char arr2[20] = "test";
my_strncat(arr2, "xy", 5); // 实际只追加"xy"
printf("短源测试: %s\n", arr2); // 输出:testxy
return 0;
}
这样就完成了对strncat的模拟使用。
7. strstr函数:

图片讲的很清楚,是找寻haystack中第一次出现needle的位置,如果还是看的不清楚,我们给出两个实例:
c
int main()
{
char *str1 = "i am a student,i want be a good student";
char *str2 = "student";
char* pch = strstr(str1, str2);
printf("%s", pch);
}

从图上可以看出是从第一处student开始打印。
c
int main() {
const char* haystack = "Hello, world! This is a test string.";
const char* needle = "world";
// 在 haystack 中查找 needle
char* result = strstr(haystack, needle);
if (result != NULL) {
printf("Substring found: %s\n", result);
}
else {
printf("Substring not found.\n");
}
return 0;
}
strstr()
函数从haystack
的开头开始查找needle
,直到找到匹配的子字符串或到达haystack
的末尾。- 如果
needle
是空字符串(""
),strstr()
会返回haystack
的起始地址。 strstr()
是区分大小写的。如果需要不区分大小写的查找,可以使用strcasestr()
(非标准函数,可能需要特定库支持)。
8.strtok函数:

将字符串 str 按分隔符集合 delim 分割成多个子串(标记),每次调用返回一个子串的指针。
参数:
str:首次调用传入待分割字符串,后续调用需传入 NULL(函数通过内部静态指针记录位置)
delim:分隔符集合(如 ",; "),任意字符匹配均触发分割
c
#include <string.h>
int main() {
char str[] = "apple,orange;banana";
const char *delim = ",;";
char *token;
// 1. 首次调用传入待分割字符串
token = strtok(str, delim);
// 2. 后续调用传入 NULL,循环获取子串
while (token != NULL) {
printf("%s\n", token);
token = strtok(NULL, delim); // 继续分割
}
return 0;
}
### **代码逐行解析**
```c
#include <string.h>
#include <stdio.h> // 补充了printf所需的头文件
int main() {
char str[] = "apple,orange;banana"; // 原始字符串(存储在可修改的数组中)
const char *delim = ",;"; // 分隔符集合(逗号和分号)
char *token; // 用于接收分割后的子串指针
// 首次调用:传入原始字符串
token = strtok(str, delim); // 找到第一个子串 "apple"
// 循环获取所有子串
while (token != NULL) {
printf("%s\n", token); // 打印当前子串(如 "apple")
token = strtok(NULL, delim); // 后续调用传入 NULL,继续分割
}
return 0;
}
**strtok()
的工作原理**
1. 首次调用:传入原始字符串
- 行为 :
- 函数从
str
开头扫描,跳过开头的分隔符(本例无) - 找到第一个非分隔符字符(
'a'
),将其作为子串起始地址。 - 继续扫描直到遇到分隔符(
,
或;
),将该分隔符替换为\0
,返回子串指针("apple"
)。
- 函数从
- 字符串变化
diff
原始: "apple,orange;banana"
修改后: "apple\0orange;banana"
↑ token 指向此处
2. **后续调用:传入 NULL
**
-
为什么必须传
NULL
?strtok()
内部通过 静态指针 记录上次分割结束的位置(即\0
后的字符)- 传入
NULL
告知函数继续从静态指针位置扫描(而非重新从头开始)。
-
第二次调用:
-
从
orange;banana
开始扫描,找到;
替换为\0
,返回"orange"
。diff修改后: "apple\0orange\0banana" ↑ token 指向此处
-
-
第三次调用:
- 从
banana
开始扫描,无更多分隔符,直接返回"banana"
- 从
3. 终止条件
-
当扫描到字符串末尾时,返回
NULL
,循环结束为何不会打印整个字符串?**
-
**
strtok()
修改了原始字符串:
它在每个分隔符位置插入\0
,将原字符串分割为多个独立子串**。例如:c"apple,orange;banana" → 被修改为: "apple\0orange\0banana"
-
**
printf("%s\n", token)
的机制:
%s
从token
指向的地址开始打印, 遇到\0
立即停止**。因此每次只打印一个子串(如"apple"
遇到第一个\0
结束)关键点 :
strtok()
仅替换作为分割点的分隔符(如,
和;
),而非所有分隔符。连续分隔符(如"a,,b"
)会被跳过,不产生空子串
内部状态与字符串变化示例
调用次数 | 参数 | 操作 | 字符串状态 | 返回值 |
---|---|---|---|---|
第一次 | str |
替换 , 为 \0 |
"apple\0orange;banana" |
"apple" |
第二次 | NULL |
替换 ; 为 \0 |
"apple\0orange\0banana" |
"orange" |
第三次 | NULL |
无分隔符,返回剩余部分 | 保持不变 | "banana" |
第四次 | NULL |
已扫描完毕 | 保持不变 | NULL |

给个关注吧