【C语言】字符串函数详解

文章目录

  • Ⅰ. strcpy -- 字符串拷贝
    • 1、函数介绍
    • 2、模拟实现
  • Ⅱ. strcat -- 字符串追加
    • 1、函数介绍
    • 2、模拟实现
  • Ⅲ. strcmp -- 字符串比较
    • 1、函数介绍
    • 2、模拟实现
  • Ⅳ. strncpy、strncat、strncmp -- 可限制操作长度
  • Ⅴ. strlen -- 求字符串长度
    • 1、函数介绍
    • 2、模拟实现(三种方式)
      • ① 计数器的方式
      • ② 递归的方式
      • ③ 指针-指针的方式
  • Ⅵ. strstr -- 字符串查找
    • 1、函数介绍
    • 2、模拟实现
  • Ⅶ. strtok -- 字符串分割
  • Ⅷ. strerror、perror -- 错误报告函数

在这里插入图片描述

Ⅰ. strcpy – 字符串拷贝

1、函数介绍

char *strcpy(char *Destination, const char *Source);

strcpy 函数是一个用于拷贝字符串的函数,即将一个字符串中的内容拷贝到另一个字符串中(会覆盖原字符串内容)。它的参数是两个指针,第一个指向的是拷贝字符串的目的地的起始位置,即要将字符串拷贝到什么地方;第二个指向的是要拷贝字符串的内容的起始位置,即需要拷贝的字符串。它的返回值是目标空间的起始位置。

💥注意:

  • 源字符串(需要被拷贝的字符串)必须以 \0 结束
  • 会将源字符串中的 \0 一同拷贝到目标空间
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变

​ 举个例子,比如我们要将 arr2 数组中的 "def" 拷贝到 arr1 数组中。

#include<stdio.h>
#include<string.h>
int main()
{char arr1[10] = "abc";char arr2[] = "def";strcpy(arr1, arr2);printf("%s", arr1);return 0;
}// 运行结果
def

注意: 拷贝结束后 arr1 数组中只有 "def",因为 "abc" 被覆盖了。

2、模拟实现

char* my_strcpy(char* dest, const char* src) 
{if (dest == NULL || src == NULL)return NULL;char* p = dest;while (*p++ = *src++) // 当遇到\0赋值之后判断为0则退出循环,达到了我们的目的{}return dest;
}

​ 在这个代码中,我们首先添加了一个错误处理机制,以防止传递给 strcpy 函数的参数是 NULL 指针。如果参数是 NULL 指针,则函数将返回 NULLmain 函数将输出一条错误消息并退出程序。

​ 此外,我们还使用了 const 关键字来指示源字符串 src 是只读的,这可以帮助我们防止无意中修改源字符串。

Ⅱ. strcat – 字符串追加

1、函数介绍

char *strcat(char *Destination, const char *Source);

strcat 函数是一个用于追加字符串的函数,即将一个字符串中的内容追加到另一个字符串后面(不会覆盖原字符串内容)。它的参数是两个指针,第一个指向的是追加字符串的目的地的起始位置,即要将字符串追加到什么地方;第二个指向的是要追加字符串的内容的起始位置,即需要追加的字符串。它的返回值是目标空间的起始位置。

💥注意:

  • 源字符串必须以 \0 结束。
  • 目标空间必须足够大,能容纳下源字符串的内容。
  • 目标空间必须可修改。
  • 字符串不能给自己追加( \0 被覆盖,无终止条件)。

​ 举个例子,比如我们要将 arr2 数组中的 “liren!” 追加到 arr1 数组的后面。

#include<stdio.h>
#include<string.h>
int main()
{char arr1[20] = "hello ";char arr2[] = "liren!";strcat(arr1, arr2);printf("%s", arr1);return 0;
}// 运行结果:
hello liren!

2、模拟实现

char* my_strcat(char* dest, const char* src) 
{char* p = dest;while (*p != '\0') // 找到目标空间的'\0'{p++;}while (*p++ = *src++) // 当遇到\0赋值之后判断为0则退出循环,达到了我们的目的{}return dest;
}

​ 这个实现中,首先定义了两个指针,一个指向目标字符串的末尾,一个指向源字符串的开头。然后,使用 while 循环将源字符串中的每个字符逐个复制到目标字符串的末尾,直到源字符串末尾为止。最后,在目标字符串的末尾添加一个 NULL 字符,表示字符串的结束。

​ 需要注意的是,在本函数的实现中,目标字符串必须具有足够的空间来容纳源字符串,否则会导致缓冲区溢出和未定义行为。此外,源字符串必须是一个 const char* 类型的指针,以防止在函数内部修改源字符串的内容。

Ⅲ. strcmp – 字符串比较

1、函数介绍

int strcmp(const char *string1, const char *string2);

strcmp 函数是一个用于比较两个字符串内容的函数。它的参数是两个指针,指针分别指向两个待比较字符串的起始位置。它的返回值是一个整型数字。当 string1 大于 string2 的时候返回一个大于 0 的数;当 string1 等于 string2 的时候返回 0;当 string1 小于 string2 的时候返回一个小于 0 的数。

💥注意:

  • 字符串比较的不是字符串长度的大小,而是两个字符串中对应位置字符的 ASCII 值。

​ 举个例子,比如比较字符串 "hello world!" 和字符串 "hello liren!" 的大小。

#include<stdio.h>
#include<string.h>
int main()
{char arr1[20] = "hello world!";char arr2[20] = "hello csdn!";printf("%d\n", strcmp(arr1, arr2));printf("%d\n", strcmp(arr2, arr1));return 0;
}// 运行结果:
1
-1

2、模拟实现

int my_strcmp(const char *s1, const char *s2) 
{while (*s1 == *s2) {if(*s1 == '\0')return 0;s1++;s2++;}return *(const unsigned char*)s1 - *(const unsigned char*)s2;
}

​ 这个实现中,使用 while 循环比较两个字符串中的每个字符。如果两个字符相等,则继续比较下一个字符;如果不相等,则返回它们的 ASCII 码之差。需要注意的是,由于有符号和无符号字符之间的比较会产生未定义行为,因此我们将两个指针都转换为 const unsigned char * 类型。

​ 如果两个字符串完全相等,则返回值为 0。如果 s1 小于 s2,则返回一个负整数,反之则返回一个正整数。

​ 需要注意的是,在使用 strcmp 函数比较字符串时,一定要确保两个字符串都以 NULL 字符结尾。如果没有以 NULL 字符结尾,会导致函数在比较过程中访问到未知的内存区域,从而导致未定义行为。

Ⅳ. strncpy、strncat、strncmp – 可限制操作长度

​ 我们发现 strcpy 是将一个字符串全部拷贝到另一个字符串,strcat 是将一个字符串全部追加到另一个字符串后面,strcmp 也是比较两个字符串的全部内容,这类操作函数称为长度不受限制的字符串操作函数。

​ 那么我们如果操作字符串时并不想操作整个字符串,而只想操作字符串的一部分怎么办呢❓❓❓

​ 库函数中的 strncpy、strncat、strncmp 便解决了这个问题。

char *strncpy(char *Dest, const char *Source, size_t count);
char *strncat(char *Dest, const char *Source, size_t count);
int strncmp(const char *string1, const char *string2, size_t count);

​ 这里就不细讲用法了,比较简单,自行尝试!

Ⅴ. strlen – 求字符串长度

1、函数介绍

size_t strlen( const char *string );

strlen 函数是一个用于求字符串长度的库函数。它的参数是被求长度的字符串的起始地址,返回值是一个无符号整型。

💥注意:

  • 参数指向的字符串要以 \0 结束。
  • strlen 返回的是在字符串中 \0 之前出现的字符个数(不包含 \0 )。
  • 注意函数的返回值为 size_t,是无符号的(易错)。

​ 举个例子,比如我们要求字符串 "abcdef" 的长度。

#include<stdio.h>
#include<string.h>
int main()
{char arr[] = "abcdef";size_t ret = strlen(arr);printf("%d\n", ret);return 0;
}// 运行结果:
6

2、模拟实现(三种方式)

① 计数器的方式

​ 我们定义一个变量为 count,如果传入的指针指向的内容不是 \0,那么 count++,同时指针后移一位,循环往复,直到找到 \0 时返回 count 即可。

size_t my_strlen(const char* str)
{size_t count = 0; // 计数器while (*str){count++;str++;}return count;
}

② 递归的方式

​ 我们一进入函数体就判断传入指针指向的内容是否为 \0,如果是就返回 0,不是就返回 1 + my_strlen(str+1),如此进行下去,直到递归到内层时找到 \0,这时再一步步将值返回回来即可。

size_t my_strlen(const char* str)
{if (*str == '\0')return 0;elsereturn 1 + my_strlen(str + 1);
}

③ 指针-指针的方式

​ 进入函数体时,我们事先定义一个指针变量将传入的指针保存下来,然后将传入的指针向后移,直到遇到 \0 时,我们返回当前指针与保存的指针的差值即可。(指针与指针的差的绝对值是两个指针之间的元素个数)

size_t my_strlen(const char* str)
{const char* p = str; // 保存起始位置while (*str != '\0')str++;return str - p;
}

Ⅵ. strstr – 字符串查找

1、函数介绍

char *strstr(const char *string, const char *strCharSet);

strstr 函数可以在一个字符串(字符串1)中查找另一个字符串(字符串2),如果字符串2存在于该字符串1中,那么就返回被字符串2在字符串1中第一次出现的起始位置,如果在字符串1中找不到字符串2,那么就返回空指针(NULL)。它的第一个参数是字符串1的起始位置,第二个参数是字符串2的起始位置。

💥注意:

  • 若字符串2为空字符串,则返回字符串1的起始位置。

​ 举个例子,比如我们在字符串 “abcdefbcd” 中查找字符串 “bcd”。

#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "abcdefbcd";char arr2[] = "bcd";char* ret = strstr(arr1, arr2); // 在arr1中查找arr2字符串第一次出现的位置if (ret != NULL)printf("%s\n", ret);elseprintf("找不到\n");return 0;
}// 运行结果:
bcdefbcd

​ 注意:strstr 函数的返回值是字符串 "bcd" 在字符串 "abcdefbcd"第一次出现的位置的起始位置,而不是出现几次就返回几个起始位置。

2、模拟实现

strstr 函数的模拟实现相对复杂,在实现过程中我们需要设置3个指针变量来辅助实现函数功能。

  • cp指针: 记录每次开始匹配时的起始位置,当从该位置开始匹配时就找到了目标字符串,便于返回目标字符串出现的起始位置;当从该位置开始没有匹配成功时,则从cp++处开始下一次的匹配。
  • p1p2指针: 通过判断p1和p2指针解引用后是否相等来判断每个字符是否匹配成功,若成功,则指针后移比较下一对字符;若失败,p1指针返回cp指针处,p2指针返回待查找字符串的起始位置。

​ 例如,在字符串"abbbcdef"中查找字符串"bbc":
​ 刚刚开始时3个指针的指向如图所示:

在这里插入图片描述

​ 若p1与p2匹配不成功,则cp指针后移,接着将cp指针赋值给p1指针:

在这里插入图片描述

​ 此时,p1与p2匹配成功,那么cp指针不动,p1和p2指针后移继续比较:

在这里插入图片描述

​ 当p1与p2匹配不成功时,cp指针后移一位,p1返回cp位置,p2返回待查找字符串起始位置:

在这里插入图片描述

​ 从此位置开始下一轮的比较:

在这里插入图片描述

​ 直到当p2指向的内容为\0时,便说明待查找字符串中的字符已经被找完,也说明了从当前cp位置开始匹配能够找到目标字符串,所以此时返回指针cp即可。

char* my_strstr(const char* str1, const char* str2)
{assert(str1 != NULL); // 断言,当str1为空指针报错assert(str2 != NULL); // 断言,当str2为空指针报错const char* cp = str1; // 记录开始匹配时的起始位置if (*str2 == '\0') // 要查找的字符串为空字符串return (char*)str1;while (*cp){const char* p1 = cp;const char* p2 = str2;while ((*p1!='\0') && (*p2!='\0') && (*p1 == *p2)){p1++;p2++;}if (*p2 == '\0') // 目标字符串已被查找完return (char*)cp;cp++;}return NULL; // 找不到目标字符串
}

​ 但是这种算法的时间复杂度比较高,优化方法就是 KMP 算法,具体可以看 KMP 算法的笔记!

Ⅶ. strtok – 字符串分割

char *strtok(char *strToken, const char *strDelimit);

strtok 函数能通过给定的一系列字符将一个字符串分割成许多子字符串的函数。它的第一个参数是需要被分割的字符串的首地址;第二个参数是一个字符串的首地址,该字符串是用作分隔符的字符集合。返回值是查找到的标记的首地址。

💥注意:

  • 找到 strToken 中的一个标记时,会将其用 \0 结尾并返回这个标记的首地址。
  • strtok 函数是会改变 strToken 的,所以在使用 strtok 函数切分的字符串都是临时拷贝的内容并且可修改。
  • 第一个参数不为 NULL 时,函数将找到 strToken 中的第一个标记,并保存它在字符串中的位置。
  • 第一个参数为 NULL 时,函数将从同一个字符串中被保存的位置开始查找它的下一个标记。
  • 若字符串中不存在更多的标记,则返回 NULL 指针。

​ 举个例子,比如我们要将字符串"2916776007@qq.com"以"@“字符和”."字符分割开。

#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "2916776007@qq.com"; // 待分割字符串char arr2[] = "@."; // 分隔符的字符集合char arr3[20] = { 0 };strcpy(arr3, arr1); // 将数据拷贝一份使用,防止原数据被修改char* token = strtok(arr3, arr2); // 第一次传参需传入待分割字符串首地址while (token != NULL) // 说明还未分割完{printf("%s\n", token);token = strtok(NULL, arr2); // 对同一个字符串进行分割,第二次及以后的第一个参数为NULL}return 0;
}// 运行结果:
2916776007
qq
com

注意:strtok 函数找到第一个标记时,将其后的 ’@‘ 字符改为 ’\0’ 并返回第一个标记的首地址,所以我们以返回的地址为首地址开始打印字符串的时候就只会打印出 2916776007,第二次再对该字符串调用 strtok 函数时将从 ’@’ 字符后面开始寻找下一个标记。

Ⅷ. strerror、perror – 错误报告函数

char *strerror(int errnum);

strerror 函数可以把错误码转换为对应的错误信息,返回错误信息对应字符串的起始地址。

void perror(const char *string);

perror 函数可以打印一个错误信息,无返回值。

​ 我们需要知道,库函数在使用的时候如果发生错误,都会有对应的错误码,而这些错误码都会被存放在 errno 这个全局变量中,如果要使用这个全局变量,我们需要引其对应的头文件:#include<errno.h>

​ 举个例子:(注:fopen函数的功能是打开一个文件,当其执行成功时会返回打开文件的首地址,执行失败时会返回一个空指针)

#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{FILE* pf = fopen("test.txt", "r");//打开test.txt文件阅读if (pf == NULL){printf("%s\n", strerror(errno));perror("fopen");}return 0;
}// 运行结果:
No such file or directory
fopen: No such file or directory

当我们要打开一个不存在的文件(test.txt)来阅读的时候,显然fopen函数会执行失败,于是pf指针接收的便是空指针(NULL)。

  • strerror: 只负责将错误码转换为对应的错误信息,不打印。
  • perror: 直接打印错误信息,并且我们可以自己加上注释来明确错误来源于哪个库函数。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/1754.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【EI 会议征稿】第四届材料工程与应用力学国际学术会议(ICMEAAE 2025)

2025 4th International Conference on Materials Engineering and Applied Mechanics 重要信息 大会官网&#xff1a;www.icmeaae.com 大会时间&#xff1a;2025年3月7-9日 大会地点&#xff1a;中国西安 截稿时间&#xff1a;2025年1月24日23:59 接受/拒稿通知&#xf…

ANSYS Fluent学习笔记(七)求解器四部分

16.亚松弛因子 Controls面板里面设置&#xff0c;它能够稳定计算的过程。如果采用常规的迭代算法可能结果就会发生振荡的情况。采用亚松驰因子可以有助于残差的稳定。 他的取值范围是0-1&#xff0c;0代表没有亚松驰&#xff0c;1表示物理量变化很快&#xff0c;一般情况下取…

【Docker】保姆级 docker 容器部署 MySQL 及 Navicat 远程连接

&#x1f970;&#x1f970;&#x1f970;来都来了&#xff0c;不妨点个关注叭&#xff01; &#x1f449;博客主页&#xff1a;欢迎各位大佬!&#x1f448; 文章目录 1. docker 容器部署 MySQL1.1 拉取mysql镜像1.2 启动容器1.3 进入容器1.4 使用 root 用户登录 2. Navicat 连…

LeetCode 热题 100_从前序与中序遍历序列构造二叉树(47_105_中等_C++)(二叉树;递归)

LeetCode 热题 100_从前序与中序遍历序列构造二叉树&#xff08;47_105&#xff09; 题目描述&#xff1a;输入输出样例&#xff1a;题解&#xff1a;解题思路&#xff1a;思路一&#xff08;递归&#xff09;&#xff1a; 代码实现代码实现&#xff08;思路一&#xff08;递归…

使用WebdriverIO和Appium测试App

1.新建项目 打开Webstorm新建项目 打开终端输入命令 npm init -y npm install wdio/cli allure-commandline --save-dev npx wdio config 然后在终端依次选择如下&#xff1a; 然后在终端输入命令&#xff1a; npm install wdio/local-runnerlatest wdio/mocha-frameworkla…

【Uniapp-Vue3】showLoading加载和showModal模态框示例

一、showLoading加载 uni.showLoading({ title:"标题", // 其他配置 }); uni.hideLoading(); showLoading开启后不会自动关闭&#xff0c;只能手动配置uni.hideLoading() 来关闭加载框。 二、showModel模态框 uni.showModel({ title:"标题", // 其他配置 …

UML系列之Rational Rose笔记八:类图

一、新建类图 首先依旧是新建要绘制的类图&#xff1b;选择class diagram&#xff1b; 修改命名&#xff1b; 二、工作台介绍 正常主要就是使用到class还有直接关联箭头就行&#xff1b; 如果不要求规范&#xff0c;直接新建一些需要的类&#xff0c;然后写好关系即可&#…

HTML应用指南:利用GET请求获取星巴克门店数据

本篇文章&#xff0c;我们将探究GET请求的实际应用&#xff0c;我们使用Python的requests库通过GET请求抓取星巴克门店信息。星巴克作为全球知名的咖啡连锁品牌&#xff0c;其门店分布广泛&#xff0c;获取这些门店的信息对于数据分析、市场研究以及商业决策都具有重要意义。我…

RV1126+FFMPEG推流项目(3)VI模块视频编码流程

视频编码的流程&#xff1a; 本章节讲的是RV1126视频编码的流程&#xff0c;在整个项目之中视频编码功能是核心之一。视频编码流程主要分三步&#xff1a;VI的初始化、VENC的初始化(硬件编码)、绑定VI和VENC节点、开启VENC线程进行视频编码的采集&#xff0c;注意一下这里的…

SimpleFOC01|基于STM32F103+CubeMX,移植核心的common代码

导言 如上图所示&#xff0c;进入SimpleFOC官网&#xff0c;点击Github下载源代码。 如上图所示&#xff0c;找到仓库。 comom代码的移植后&#xff0c;simpleFOC的移植算是完成一大半。simpleFOC源码分为如下5个部分&#xff0c;其中communication是跟simpleFOC上位机通讯&a…

【2025最新】机器学习类计算机毕设选题80套,适合大数据,人工智能

【2025最新】机器学习类型计算机毕设选题 1-10套 基于Spring Boot的物流管理系统的设计与实现 基于机器学习的虚假招聘信息的分析与预测 基于机器学习的影响数据科学家职业变动因素的分析与预测 基于Spring Boot的历史文物交流平台的设计与实现 基于机器学习的肥胖影响因素的分…

金融项目实战 02|接口测试分析、设计以及实现

目录 ⼀、接口相关理论 二、接口测试 1、待测接口&#xff1a;投资业务 2、接口测试流程 3、设计用例理论 1️⃣设计方法 2️⃣工具 4、测试点提取 5、测试用例&#xff08;只涉及了必测的&#xff09; 1️⃣注册图⽚验证码、注册短信验证码 2️⃣注册 3️⃣登录 …

C++:string

一、string概念 之前介绍过通过字符数组保存字符串&#xff0c;然后对字符数组中的字符串做各种操作&#xff1b;为了更加简单方便&#xff0c;在C中&#xff0c;又增加了 string 来处理字符串。 char str[20] "hello world"; string 字符串其实是一种更加高级的封…

STORM:从多时间点2D图像中快速重建动态3D场景的技术突破

随着计算机视觉和机器学习技术的迅猛发展,我们已经能够利用AI来解决许多复杂的问题。然而,在处理大规模室外动态3D场景重建时,现有的方法往往面临着诸多挑战,如需要大量人工标注数据、处理速度慢以及难以准确捕捉移动物体等。为了解决这些问题,研究者们开发了STORM(Spati…

C#使用OpenTK绘制3D可拖动旋转图形三棱锥

接上篇,绘制着色矩形 C#使用OpenTK绘制一个着色矩形-CSDN博客 上一篇安装OpenTK.GLControl后,这里可以直接拖动控件GLControl 我们会发现GLControl继承于UserControl //// 摘要:// OpenGL-aware WinForms control. The WinForms designer will always call the default//…

Vue2+OpenLayers使用Overlay实现点击获取当前经纬度信息(提供Gitee源码)

目录 一、案例截图 二、安装OpenLayers库 三、代码实现 关键参数&#xff1a; 实现思路&#xff1a; 核心代码&#xff1a; 完整代码&#xff1a; 四、Gitee源码 一、案例截图 二、安装OpenLayers库 npm install ol 三、代码实现 覆盖物&#xff08;Overlay&#xf…

浅谈云计算12 | KVM虚拟化技术

KVM虚拟化技术 一、KVM虚拟化技术基础1.1 KVM虚拟化技术简介1.2 KVM虚拟化技术架构1.2.1 KVM内核模块1.2.2 用户空间工具&#xff08;QEMU、Libvirt等&#xff09; 二、KVM虚拟化技术原理2.1 硬件辅助虚拟化2.2 VMCS结构与工作机制 三、KVM虚拟化技术面临的挑战与应对策略3.1 性…

【2024年华为OD机试】(C卷,100分)- 分割均衡字符串 (Java JS PythonC/C++)

一、问题描述 题目描述 均衡串定义&#xff1a;字符串中只包含两种字符&#xff0c;且这两种字符的个数相同。 给定一个均衡字符串&#xff0c;请给出可分割成新的均衡子串的最大个数。 约定&#xff1a;字符串中只包含大写的 X 和 Y 两种字符。 输入描述 输入一个均衡串…

【update 更新数据语法合集】.NET开源ORM框架 SqlSugar 系列

系列文章目录 &#x1f380;&#x1f380;&#x1f380; .NET开源 ORM 框架 SqlSugar 系列 &#x1f380;&#x1f380;&#x1f380; 文章目录 系列文章目录前言 &#x1f343;一、实体对象更新1.1 单条与批量1.2 不更新某列1.3 只更新某列1.4 NULL列不更新1.5 无主键/指定列…

项目概述、开发环境搭建(day01)

软件开发整体介绍 软件开发流程 第1阶段: 需求分析 需求规格说明书&#xff0c; 一般来说就是使用 Word 文档来描述当前项目的各个组成部分&#xff0c;如&#xff1a;系统定义、应用环境、功能规格、性能需求等&#xff0c;都会在文档中描述。产品原型&#xff0c;一般是通过…