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

news2025/1/15 11:07:36

文章目录

  • Ⅰ. 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;
	else
		return 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);
	else
		printf("找不到\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.coloradmin.cn/o/2276969.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IO进程day6

一、思维导图 二、练习题1 有一个隧道&#xff0c;长1000m&#xff0c;有一辆高铁&#xff0c;每秒100米&#xff0c;有一辆快车&#xff0c;每秒50m 要求模拟这两列火车通过隧道的场景。 #include <stdio.h> #include <unistd.h> #include <pthread.h>pthre…

手撕代码: C++实现按位序列化和反序列化

目录 1.需求 2.流程分析 3.实现过程 4.总结 1.需求 在我们正在开发的项目&#xff0c;有这样一种需求&#xff0c;实现固定格式和自由格式的比特流无线传输。解释一下&#xff0c;固定格式形如下面表格&#xff1a; 每个字段都有位宽、类型等属性&#xff0c;这种固定格式一…

期望最大化算法:机器学习中的隐变量与参数估计的艺术

引言 在机器学习和统计学领域&#xff0c;许多实际问题涉及到含有隐变量的概率模型。例如&#xff0c;在图像识别中&#xff0c;图像的语义信息往往是隐变量&#xff0c;而我们能观测到的只是图像的像素值&#xff1b;在语音识别中&#xff0c;语音对应的文本内容是隐变量&…

2025封禁指定国家ip-安装xtables-addons记录

如何安装和使用 安装lux仓库(该仓库包含xtables-addons所需的依赖环境) # wget http://repo.iotti.biz/CentOS/7/noarch/lux-release-7-1.noarch.rpm # rpm -ivh lux-release-7-1.noarch.rpm 安装xtables-addons。注意&#xff1a;必须先安装kmod-xtables-addons&#xff0c;再…

使用C语言实现栈的插入、删除和排序操作

栈是一种后进先出(LIFO, Last In First Out)的数据结构,这意味着最后插入的元素最先被删除。在C语言中,我们可以通过数组或链表来实现栈。本文将使用数组来实现一个简单的栈,并提供插入(push)、删除(pop)以及排序(这里采用一种简单的排序方法,例如冒泡排序)的操作示…

【Go】Go Gin框架初识(一)

1. 什么是Gin框架 Gin框架&#xff1a;是一个由 Golang 语言开发的 web 框架&#xff0c;能够极大提高开发 web 应用的效率&#xff01; 1.1 什么是web框架 web框架体系图&#xff08;前后端不分离&#xff09;如下图所示&#xff1a; 从上图中我们可以发现一个Web框架最重要…

【深度学习】Windows系统Anaconda + CUDA + cuDNN + Pytorch环境配置

在做深度学习内容之前&#xff0c;为GPU配置anaconda CUDA cuDNN pytorch环境&#xff0c;在网络上参考了很多帖子&#xff0c;但pytorch的安装部分都有些问题或者比较复杂繁琐&#xff0c;这里总结了相对简单快速的配置方式 文章目录 AnacondaCUDAcuDNNpytorchtorchtorchau…

提供的 IP 地址 10.0.0.5 和子网掩码位 /26 来计算相关的网络信息

网络和IP地址计算器 https://www.sojson.com/convert/subnetmask.html提供的 IP 地址 10.0.0.5 和子网掩码位 /26 来计算相关的网络信息。 子网掩码转换 子网掩码 /26 的含义二进制表示:/26 表示前 26 位是网络部分&#xff0c;剩下的 6 位是主机部分。对应的子网掩码为 255…

js中的Object.defineProperty()详解

文章目录 一、Object.defineProperty()二、descriptor属性描述符2.1、数据描述符2.2、访问器描述符2.3、descriptor属性2.3.1、value2.3.2、writable2.3.3、enumerable &#xff08;可遍历性&#xff09;2.3.4、configurable &#xff08;可配置性&#xff09; 三、注意事项 一…

单细胞组学大模型(8)--- scGenePT,scGPT和GenePT的结合,实验数据和文本数据的交融模型

–https://doi.org/10.1101/2024.10.23.619972 研究团队和单位 Theofanis Karaletsos–Head Of AI - Science at Chan Zuckerberg Initiative &#xff08;Chan Zuckerberg Initiative是扎克伯格和他妻子Chan成立的科研&教育机构&#xff09; 研究简介 研究背景&…

微信原生小程序自定义封装组件(以导航navbar为例)

封装 topnav.js const App getApp(); Component({// 组件的属性列表properties: {pageName: String, //中间的titleshowNav: { //判断是否显示左上角的按钮 type: Boolean,value: true},showHome: { //判断是否显示左上角的home按钮type: Boolean,value: true},showLocat…

day06_Spark SQL

文章目录 day06_Spark SQL课程笔记一、今日课程内容二、DataFrame详解&#xff08;掌握&#xff09;5.清洗相关的API6.Spark SQL的Shuffle分区设置7.数据写出操作写出到文件写出到数据库 三、Spark SQL的综合案例&#xff08;掌握&#xff09;1、常见DSL代码整理2、电影分析案例…

HarmonyOS NEXT应用开发边学边玩,从零实现一影视APP(四、最近上映电影滚动展示及加载更多的实现)

在HarmonyOS NEXT开发环境中&#xff0c;我们可以使用多种组件和库来构建丰富且交互友好的应用。本文将展示如何使用HarmonyOS NEXT框架和nutpi/axios库&#xff0c;从零开始实现一个简单的影视APP的首页&#xff0c;主要关注最近上映电影的滚动展示及加载更多功能的实现。 安装…

快速、可靠且高性价比的定制IP模式提升芯片设计公司竞争力

作者&#xff1a;Karthik Gopal&#xff0c;SmartDV Technologies亚洲区总经理 智权半导体科技&#xff08;厦门&#xff09;有限公司总经理 无论是在出货量巨大的消费电子市场&#xff0c;还是针对特定应用的细分芯片市场&#xff0c;差异化芯片设计带来的定制化需求也在芯片…

基础入门-抓包技术HTTPS协议APP小程序PC应用Web证书信任转发联动

知识点&#xff1a; 1、抓包技术-Web应用-http/s-Burp&Yakit 2、抓包技术-APP应用-http/s-Burp&Yakit 3、抓包技术-PC端应用-http/s-Burp&Yakit 4、抓包技术-WX小程序-http/s-Burp&Yakit 5、抓包技术-软件联动-http/s-Proxifier 6、抓包技术-通用方案-http/s-R…

贪心算法详细讲解(沉淀中)

文章目录 1. 什么是贪心算法&#xff1f;&#xff08;贪婪鼠目寸光&#xff09;经典例题1.1.1 找零问题1.1.2最小路径和1.1.3 背包问题 2.贪心算法的特点2.1 证明例1 3.学习贪心的方向心得体会 1. 什么是贪心算法&#xff1f;&#xff08;贪婪鼠目寸光&#xff09; 贪心策略&a…

SQL面试题2:留存率问题

引言 场景介绍&#xff1a; 在互联网产品运营中&#xff0c;用户注册量和留存率是衡量产品吸引力和用户粘性的关键指标&#xff0c;直接影响产品的可持续发展和商业价值。通过分析这些数据&#xff0c;企业可以了解用户行为&#xff0c;优化产品策略&#xff0c;提升用户体验…

学会使用开源软件jclasslib 字节码文件的组成 详解

应用场景 1 应用场景 2 学习路线 以正确的姿势打开文件 字节码文件的组成 玩转字节码常用工具 以正确的姿势打开文件 开源软件 jclasslib github 地址 https://github.com/ingokegel/jclasslib 工具使用 字节码文件的组成 基本信息 常量池 字段 方法 属性 详解 魔数 主副版…

primitive 的 Appearance编写着色器材质

import { nextTick, onMounted, ref } from vue import * as Cesium from cesium import gsap from gsaponMounted(() > { ... })// 1、创建矩形几何体&#xff0c;Cesium.RectangleGeometry&#xff1a;几何体&#xff0c;Rectangle&#xff1a;矩形 let rectGeometry new…

详情页 路由传值

路由传值获取参数 渲染数据 主页面 <template><div class"km"><div v-for"item in items" :key"item.id"><div class"title-km" ><img :src"item.imageUrl" alt"Image" class"…