当前位置: 首页 > news >正文

字符操作函数续上

5. strncpy函数:

5.1 strncpy的使用:

在这里插入图片描述

#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的模拟使用:

我们来尝试使用代码来复原:

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的使用:

在这里插入图片描述
strncatstrcat一样都是追加字符到原来的目标字符串;要求目标的字符串需要足够的空间,并返回目标字符串的开头的地址。我们来尝试以下如何使用:

int main()
{char* a = "world";char arr[40] = "学习c,hello ";strncat(arr, a, 5);printf("%s", arr);
}

这一小段代码就可以完成对arr这个字符串的追加,我们这次来深究以下arr字符串:

strncat() 的工作原理​
  1. 定位目标字符串的结尾
    • 函数首先找到目标字符串 dest 的结尾(即 dest\0 位置)。
    • 例如:若 dest = "Hello",则从 'o' 后的 \0 处开始追加。
  2. 追加字符并处理终止符
    • 情况 1:n ≤ 源字符串长度
      复制源字符串 src 的前 n 个字符到 dest 末尾,然后自动追加一个 \0
      示例
      char dest[10] = "Hi";
      strncat(dest, "World", 3); // 结果:"HiWor\0"
      
      • 复制 'W''o''r' 后,函数自动添加 \0
    • 情况 2:n > 源字符串长度
      复制整个 src(包括其自身的 \0),并\0 填充剩余空间直到总追加字符数达到 n
      示例
      char dest[10] = "A";
      strncat(dest, "BC", 5);    // 结果:"ABC\0\0\0"
      
      • 源字符串 "BC" 长度=2(含 \0),追加后补充 3 个 \0 至总长度 5。
  3. 返回值
    返回目标字符串 dest 的指针,便于链式调用。​
特性strncat()strncpy()
终止符处理总是追加 \0仅在 src 长度 < n 时填充 \0
填充行为仅填充至总追加字符数为 n\0 填满整个 n
目标缓冲区修改起点dest 的结尾 \0dest 的起始位置
安全性更高(强制终止)需手动添加 \0,否则可能出错

6.2strncat的模拟使用:

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);
}

我的第一版代码是有错误的;没有想到​未处理源字符串提前结束,这会导致会越界访问,还有逻辑上的麻烦处理了,因此我们需要做出以下的改变:

#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:源字符串短于numchar arr2[20] = "test";my_strncat(arr2, "xy", 5);   // 实际只追加"xy"printf("短源测试: %s\n", arr2);  // 输出:testxyreturn 0;
}

这样就完成了对strncat的模拟使用。

7. strstr函数:

在这里插入图片描述
图片讲的很清楚,是找寻haystack中第一次出现needle的位置,如果还是看的不清楚,我们给出两个实例:

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开始打印。

int main() {const char* haystack = "Hello, world! This is a test string.";const char* needle = "world";// 在 haystack 中查找 needlechar* 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:分隔符集合(如 ",; "),任意字符匹配均触发分割

#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")。
  • 字符串变化
原始: "apple,orange;banana"
修改后: "apple\0orange;banana"↑ token 指向此处
2. ​**后续调用:传入 NULL**​
  • 为什么必须传 NULL?​

    • strtok() 内部通过 ​静态指针​ 记录上次分割结束的位置(即 \0 后的字符)
    • 传入 NULL 告知函数继续从静态指针位置扫描(而非重新从头开始)。
  • 第二次调用​:

    • orange;banana 开始扫描,找到 ; 替换为 \0,返回 "orange"
      修改后: "apple\0orange\0banana"↑ token 指向此处
      
  • 第三次调用​:

    • banana 开始扫描,无更多分隔符,直接返回 "banana"
3. ​终止条件
  • 当扫描到字符串末尾时,返回 NULL,循环结束
    为何不会打印整个字符串?​**​

  • ​**strtok() 修改了原始字符串​:
    它在每个分隔符位置插入 \0,将原字符串
    分割为多个独立子串**。例如:

    "apple,orange;banana" 
    → 被修改为: "apple\0orange\0banana"
    
  • ​**printf("%s\n", token) 的机制​:
    %stoken 指向的地址开始打印,​
    遇到 \0 立即停止**。因此每次只打印一个子串(如 "apple" 遇到第一个 \0 结束)
    关键点​:strtok() 仅替换作为分割点的分隔符(如 ,;),而非所有分隔符。连续分隔符(如 "a,,b")会被跳过,不产生空子串

内部状态与字符串变化示例

调用次数参数操作字符串状态返回值
第一次str替换 ,\0"apple\0orange;banana""apple"
第二次NULL替换 ;\0"apple\0orange\0banana""orange"
第三次NULL无分隔符,返回剩余部分保持不变"banana"
第四次NULL已扫描完毕保持不变NULL

在这里插入图片描述
给个关注吧

http://www.lryc.cn/news/571327.html

相关文章:

  • 图扑 HT 3D 场景视频嵌入应用功能
  • cuda编程笔记(4)--纹理内存
  • OpenCV——图像形态学
  • Docker 快速搭建一个基于 GPT-Vis 组件的统计图表生成服务
  • 【超详细】讯飞智能车PC电脑烧录指南(高级系统部署与恢复)
  • 系统思考:越用力推系统,系统反弹性越大
  • Flask入门指南:从零构建Python微服务
  • Appium环境安装
  • 关于人工智能未来的趋势
  • B站PWN教程笔记-12
  • 计算机视觉| 分割大模型Segment Anything(SAM)从0到1使用
  • Muon:神经网络隐藏层的革命性优化器
  • 从零到一:C语言基础入门学习路线与核心知识点全解析
  • 香橙派3B学习笔记12:C语言操作GPIO_<wiringPi.h>_点灯通用输入输出
  • FPGA 44 ,SDC 时序约束标准( 深度解析 SDC 标准 )
  • 期末作业swing水果店管理系统
  • 二分算法深度解析
  • 简说 python
  • C++ vector(2)
  • 【编译工具】CodeRider 2.0:驭码 CodeRider 2.0 全流程智能研发协作平台深度技术测评报告
  • Java在IDEA中终端窗口输出正常,但打包成JAR后中文乱码问题
  • 『大模型笔记』第3篇:多长的 Prompt 会阻塞其他请求?优化策略解析
  • Java线程池全面解析:原理、实现与最佳实践
  • Socket 编程 UDP
  • 【Linux】UDP与TCP协议
  • Kubernetes RDMA 概述与实战(大模型场景)
  • UE5 游戏模板 —— Puzzle 拼图游戏
  • 【配置教程】新版OpenCV+Android Studio环境配置(4.11测试通过)
  • 在线教学课程视频AI智能大纲代码与演示
  • 【Docker安装PostgreSQL】psql:致命错误: 用户 Password 认证失败