C语言——字符串函数

news2024/11/16 13:24:04

一.前言

我们在日常写代码的过程中,经常会对字符串进行处理的过程。而在C语言中的<string.h>中,包含了众多字符串函数,我们可以借助这些字符串函数来对其进行各种操作。

二.strlen函数

strlen函数的作用是求出所传字符串的长度。该函数的参数待求长度的字符串的地址,返回值为size_t类型的整数,因为一个字符串的长度不可能是负数,所以可以用无符号整型数据接收。该函数的逻辑是:从所传字符串首地址开始向后寻找,直到遇到'\0'结束,后返回'\0'到首地址之间的字符个数。

#include <stdio.h>
#include <string.h>

int main()
{
	int arr[] = "abcdef";
	size_t len = strlen(arr);
	printf("%zd\n", len);
	return 0;
}

使用strlen函数的时候得包含对应头文件<string.h>。有上述代码可知,len的长度为6。注意:打印size_t类型的数据时,用%zd来打印。

2.1strlen函数的模拟实现

我们已经知道了strlen函数的使用方法,现在我们来模拟它的实现。要想实现它,我们得知道它的运行逻辑:我们先给它传了一个字符串的首地址,它就沿着该地址向后寻找,直到遇到'\0'停止,然后返回首地址与'\0'之间的字符个数。它的实现逻辑并不复杂,现在我们来模拟实现。

1.计数器法

我们可以创建一个变量用来统计字符的个数,然后通过while循环遍历该字符串的每一个字符,遇到'\0',则表示字符串遍历完毕,返回计数器。注意:当我们要使用该字符串时,应先判断该字符串是否为空。所以我们可以使用assert断言,使用assert得包含头文件<assert.h>

//1.计数器法

#include <stdio.h>
#include <assert.h>
size_t my_strlen(const char* arr)
{
    assert(arr);//判断传入的地址是否为空
	size_t count = 0;
	while (*arr != '\0')
	{
		count++;
		arr++;
	}
	return count;
}

int main()
{
	char arr[] = "abcdef";
	size_t len = my_strlen(arr);
	printf("%zd\n", len);
	return 0;
}

2.不创建临时变量法(递归法)

不能创建临时变量,)意味着不能再使用计数器的方式来统计。这时,我们可以利用递归法来实现。这里我们在使用待求字符串前依旧得进行断言,判断地址是否有效。

//2.不能创建临时变量,递归法

#include <stdio.h>
#include <assert.h>

size_t my_strlen(const char* arr)
{
	assert(arr);//判断所传字符串是否为NULL
	if (*arr != '\0')
	{
		return 1 + my_strlen(arr + 1);
	}
	else
	{
		return 0;
	}
}

int main()
{
	char arr[] = "abcdef";
	size_t len = my_strlen(arr);
	printf("%zd\n", len);
	return 0;
}

3.指针-指针

我们知道,两指针可以进行相减的操作,得出的结果的绝对值表示的是两指针之间的元素个数。所以我们可以将字符串首地址储存起来,在进行遍历,找到字符串的末尾,两者相减就是该字符串的元素个数。

//3.指针-指针

#include <stdio.h>
#include <assert.h>

size_t my_strlen(const char* arr)
{
	assert(arr);//判断所传地址是否为NULL

	char* p1 =(char*) arr;//记录首地址
	char* p2 = NULL;//记录末尾

	while (*arr++ != '\0')
	{
		;
	}
	p2 = (char*)arr - 1;//当退出循环后,arr指向的'\0'的位置,所以字符串的末尾应该是arr-1
	return p2 - p1;
}

int main()
{
	char arr[] = "abcdef";
	size_t len = my_strlen(arr);
	printf("%zd\n", len);
	return 0;
}

三.strcpy函数

strcpy函数的参数为两个char*的指针,返回值也是char*类型的。该函数的作用为:将第二个指针所对应的字符串复制到第一个指针所指向的内容里。但复制的前提是目标地址的内存空间足够大。

#include <stdio.h>
#include <string.h>

int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = "";
	char * ret = strcpy(arr2, arr1);
	printf("%s\n", ret);
	return 0;
}

打印结果为:

哪有的人就会问了,将第二个字符串复制给第一个指针是否会将第二个字符串的'\0'也复制过去?我们可以调试来看一下。

我们先将目标地址的字符串全部设置成x,并且x的数量是多于来源字符串的。如果我们在监视窗口看到前6个x被复制成abcdef,而第七个x被复制成'\0',那么就能说明strcpy复制的时候会将来源字符串的全部字符包括结尾的'\0'也复制给目标字符串。

这时我们可以清楚的看到,当我们执行了strcpy函数之后,它会将来源字符串的全部字符包括结尾的‘\0’全部复制给目标字符串。

注意:对于strcpy函数来说有几个注意点:

1.它会将来源字符串的全部包括'\0'都复制给目标

2.来源字符串必须包含'\0'

3.目标地址必须够大,以确保能够存放的下来源字符串

4.目标空间必须可以修改

3.1.strcpy函数的模拟实现

为了实现strcpy函数,我们就得清楚其复制的逻辑:strcpy是将第二个来源字符串的全部字符包括'\0'也复制给目标字符串。所以我们可以从两个指针的首地址开始,将来源字符串的内容依次赋给目标字符串。既然是模拟实现strcpy函数,所以我们在函数返回值和参数上应保持一致,目标地址为char*类型,而来源地址为const char*类型。因为我们只希望将第二个指针的内容复制给一指针,并不希望二指针的内容被修改,于是用const修饰。

//strcpy函数的模拟实现

#include <stdio.h>
#include <assert.h>

char* my_strcpy(char* destination, const char* source)
{
	assert(destination && source);//判断传来的地址是否为空
	
	char* ret = destination;//记录目标字符串的首地址

	while (*destination++ = *source++)//将来源字符串的每个字符赋给目标字符串
	{
		;
	}
	return ret;
}

int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = { 0 };
	char* ret = my_strcpy(arr2, arr1);
	printf("%s\n", ret);
	return 0;
}

在使用两个指针之前,我们需要断言,判断两指针是否为NULL。在此之后,我们先创建一个指针用来存储目标地址的首地址。后利用循环,对目标地址进行赋值,随后指针++,两指针走到下一个位置,后继续进行赋值,直到将来源地址的'\0'也赋值给目标地址后,循环判断条件为假,退出循环,复制结束,后将之前存放来目标地址的首地址的指针返回。

四.strcat函数

strcat函数的返回值为char*类型,参数分别为char*类型的目标字符串和const char* 类型的源字符串。它的功能是将目标字符串和源字符串连接起来(将源字符串复制在目标字符串的后面)。

#include <stdio.h>
#include <string.h>

int main()
{
	char arr1[20] = "abcdef";//目标字符串
	char arr2[] = "ghi";//来源字符串

	char* ret = strcat(arr1, arr2);
	
	printf("%s\n", ret);
	return 0;
}

运行结果为:

那是否可以利用该函数对字符串本身进行追加呢?

我们看到当我们在给字符串追加自己时,在调试的时候就会报错。说发生了访问冲突,其实就是数组越界了。这是因为你在给自己追加的时候,字符串在同时的发生变化。

我们看到,当指针指向arr数组的第一个元素时,会将它追加在目标字符串的位置,而目标字符串和源字符串是同一个,所以两者都发生了改变。随后指针向后走,指向源字符串的第二个元素,将其追加到目标字符串的后面,两者又同时发生改变。我们这时发现,字符串中已经没有了\0,故追加不会停止,直到追加到大于目标字符串可以承受的最大元素,导致越界。所以我们得出结论,不可以给字符串追加自身。

我们在使用strcat函数时,有几个注意点:

1.源字符串必须以\0结束;

2.目标字符串也必须包含\0,否则不知道从什么位置开始追加;

3.目标地址的空间必须足够大,能够容纳源字符串;

4.目标空间必须得可修改;

5.最好不要给自身追加,否则可能导致越界访问

4.1.strcat函数的模拟实现

模拟实现之前,我们得知道strcat函数到底是怎么个逻辑来实现的追加字符串。我们上面说到源字符串必须以\0结尾,所以说明当对源字符串解引用的时候等于\0就说明已经将源字符串全部追加到了目标字符串;上面还提到目标字符串也必须含有\0,否则不知道从哪里开始追加。所以要实现该函数,我们就得找到目标字符串的第一个\0位置,以此开始进行追加,直到将源字符串的所有字符都追加到目标字符串,就表明追加完成。

#include <stdio.h>
#include <string.h>
#include <assert.h>

char* my_strcat(char* destination, const char* source)
{
	assert(destination && source);//判断所传两指针是否为NULL

	char* ret = destination;//将目标字符串的首地址储存起来

	//找到目标字符串中第一次出现\0的位置
	while (*destination)
	{
		destination++;
	}

	//从该位置开始,将源字符串追加到目标字符串
	while (*destination++ = *source++)
	{
		;
	}

	return ret;
}

int main()
{
	char arr1[30] = "abcdef";//目标字符串
	char arr2[] = "ghijk";//源字符串

	char * ret = my_strcat(arr1, arr2);

	printf("%s\n", ret);
	return 0;
}

运行结果:

我们在使用指针的时候最好先进行判断,以防该指针为NULL。

五.strcmp函数

strcmp也是C语言中字符串函数之一,它的参数为两个 const char*类型的指针,返回值为int。该函数的作用为:比较两字符串的大小。返回值的大小由比较结果来定。如果:str1 > str2,则返回大于0的数字;str1 = str2,则返回0;str2<str2,则返回小于0的数字。

那strcmp是如何比较两个字符串呢?

strcmp函数进行两字符串间的比较是同位之间进行比较,比较的是字符所对应的ASCII码值,ASCII码值大的该字符就大。

需要注意的是,字符串的大小与其长度没有关系,只跟相同位置上的字符所对应的ASCII码值有关。

5.1.strcmp函数的模拟实现

我们在介绍strcmp函数的时候就已经解释它的比较逻辑,我们只需要根据其比较逻辑写出代码即可:

#include <stdio.h>
#include <assert.h>

int my_strcmp(const char* p1, const char* p2)
{
	//使用指针前先对其进行断言,判断其是否可用
	assert(p1 != NULL);
	assert(p2 != NULL);

	while (*p1 == *p2)
	{
		//如果出现\0,说明两个字符串完全相同
		if (*p1 == '\0')
		{
			return 0;
		}

		//使指针后移,指针下一个元素
		p1++;
		p2++;
	}

	//字符减法相当于用ASCII码值进行减法,如果p1>p2,则返回一个大于0的数,反之返回一个小于0的数
	return *p1 - *p2;
}

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcdef";

	int ret = my_strcmp(arr1, arr2);

	printf("%d\n", ret);
	return 0;
}

六.strncpy函数

从函数名可以看出来,它与strcpy只有一个n的区别,那区别究竟在哪里呢?

我们发现,两者的区别就在于参数的不同,strncpy比strcpy多了一个参数 size_t 类型的num。这个参数其实就是n。

该函数的作用与strcpy类似,是复制源字符串的前n个元素到目标地址。

那如果源字符串没有num个元素,为怎么样呢?

我们看到,arr2只有五个元素,如果num=5的话,刚好把arr1中前五个x改变,后面不改变;但当num =6的时候,arr2不够6个元素,此时进行复制的时候,会在第五个元素后面补0,直到够num个元素。

而对于字符数组来说,0就相当于\0,所以arr1中第六个元素的位置就会被复制成\0,导致不能打印后面的x。

注意:在使用strncpy函数的时候同样得保证目标地址的内存空间足够大。

七.strncat函数

我们看到,两者的区别也在于一个size_t类型的num参数。该函数的功能就是追加源字符串中的前num个字符到目标地址。

与strncpy相同,如果num大于源字符串的长度,就会在后面补0:

注意:目标地址的内存大小必须足够大。

八.strncmp函数

该函数中的size_t num,与上面两者有点不同,num的作用是比较str1 和 str2 的前num个字符。

如图所示,如果比较的是前四个字符,则arr1和arr2相同,返回0;如果比较前5个字符,则arr1 < arr2,返回小于0的值。

 九.strstr函数

strstr函数的功能是在str1中查找str2,如果找到了,就返回str2在str1中第一个字符出现的地址;如果找不到则返回NULL。

9.1strstr函数的模拟实现


#include <stdio.h>
#include <assert.h>

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);

    //如果str2为空字符串,则直接返回str1
	if (!*str2)
	{
		return (char*)str1;
	}

	char* ch = (char*)str1;
	char* s1;
	char* s2;

	while (*ch)
	{
		s1 = ch;
		s2 = (char*)str2;

		//s1、s2都不是\0,并且两者相等
		while (*s1 && *s2 && !(*s1 - *s2))
		{
			//相等之后,指针向后走继续比较
			s1++;
			s2++;
		}

		//在此判断*s2是否为\0,若为\0则说明已经在str1中找到了str2
		if (!*s2)
		{
			return ch;
		}

		ch++;
	}

	//如果ch遇到\0,说明已经将str1查找完了,也没有找到str2,这时返回NULL
	return NULL;
}

int main()
{
	char arr1[] = "This is a apple";
	char arr2[] = "is";

	char* ret = my_strstr(arr1, arr2);

	printf("%s\n", ret);
	return 0;
}

十.strtok函数

strtok函数的功能为将str分解成一系列字符串,以其中的分隔符来进行分离。delimiters指的是所有分隔符所存在的字符串。下面给出一个例子:

值得注意的是,在使用完一次strtok函数之后,它会记住第一次分隔符出现的地方,下次调用的时候第一个参数传一个空指针,第二个参数依旧时分隔符字符串,该函数会从第一次分隔符的下一个位置开始扫描。

但是如果我们想要分开一个字符串就得写多个这样的调用和输出语句,非常麻烦,我们可以利用循环来实现多次调用及打印:

法一:

#include <stdio.h>
#include <string.h>

int main()
{
	char arr[] = "xia-si-cheng.12138.com";
	char deli[] = "-.";

	char* str = NULL;
	for (str = strtok(arr,deli); str != NULL;str = strtok(NULL,deli))
	{
		printf("%s\n", str);
	}
	return 0;
}

法二:


#include <stdio.h>
#include <string.h>

int main()
{
	char arr[] = "xia-si-cheng.12138.com";
	char deli[] = "-.";

	char* ret = strtok(arr, deli);
	
	while (ret)
	{
		printf("%s\n", ret);

		ret = strtok(NULL, deli);
	}
	return 0;
}

注意:使用strtok函数后str1的内容会改变,所有分隔符都会被修改成\0。

十一.strerror函数

该函数的功能是返回错误信息,通过错误数字代码,返回一个错误信息指针,指针所指向的内容就是错误信息。该错误信息取决于开发平台和编译器。

总结:

字符串函数的内容就到此结束,如果文章出错还请大家帮我揪出问题。

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

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

相关文章

图片改大小尺寸怎么改?几个修改图片尺寸的方法

日常生活和工作中&#xff0c;图片的大小和尺寸对于我们的工作和生活都至关重要&#xff0c;因此我们经常需要调整图片的大小。我们都知道压缩图是一款功能强大的图片在线处理工具&#xff0c;那么用它怎么调整图片大小呢&#xff1f;下面就让我们一起来看一下具体的操作步骤。…

网络与并发编程(二)

线程_信号量 互斥锁使用后&#xff0c;一个资源同时只有一个线程访问。如果某个资源&#xff0c;我们同时想让N个(指定数值)线程访问&#xff1f;这时候&#xff0c;可以使用信号量。 信号量控制同时访问资源的数量。信号量和锁相似&#xff0c;锁同一时间只允许一个对象(进程…

Python项目21:一个简单的记账系统(收入+支出+查询)

------------★Python练手项目源码★------------ Python项目源码20&#xff1a;银行管理系统&#xff08;开户、查询、取款、存款、转账、锁定、解锁、退出&#xff09; Python项目19&#xff1a;学员信息管理系统&#xff08;简易版&#xff09; Python项目18&#xff1a;…

IDEA配置本地Maven(解决依赖下载缓慢)

1.下载Maven Maven下载页 根据需要选择下载其中一个&#xff0c;我选了zip格式的 将下载好的apache-maven-3.9.5解压到你想要的目录下 2.配置系统环境 设置系统环境变量 MAVEN_HOME 为安装路径的bin目录 变量名&#xff1a;MAVEN_HOME 变量值&#xff1a;写你的 apache-m…

远程登录服务器(ubuntu20.04)在自己账号下的虚拟环境(python3.6)安装Jupyter并连接pycharm使用

参考&#xff1a;Jupyter notebook/lab安装及远程访问 1、安装jupyter pip install notebook遇到的问题&#xff1a; &#xff08;1&#xff09;运行这个指令之前尝试了好多方法都安不上 此前还尝试了更新pip之类的&#xff0c;大家安不上也可以先更新pip试试。 &#xff0…

list的常用接口底层实现与介绍

目录 概念&#xff1a; list的基本结构&#xff1a; list的迭代器⭐❤&#xff1a; 自定义类型的完善&#xff1a; const的迭代器&#xff1a; insert erase&#xff1a; size empty push_back 、push_front 、pop_back、pop_front swap 、operator 析构函数…

记录Http访问服务接口出现 301 Moved Permanently

记录Http访问服务接口出现 301 Moved Permanently 一、项目背景 ​ 在A服务中 需要通过远程调用 B服务接口 获取数据。A项目与B项目部署在不同的服务接口中。 请求接口响应界面 ​ 在调用B项目的接口时&#xff0c;响应的数据一直为 301 Moved Permanently Html代码&#x…

【星计划★C语言】c语言初相识:探索编程之路

&#x1f308;个人主页&#xff1a;聆风吟_ &#x1f525;系列专栏&#xff1a;星计划★C语言、Linux实践室 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. ⛳️第一个c语言程序二. ⛳️数据类型2.1 &#x1f514;数据单位2.2 &…

2024福建三支一扶报名流程,超全超详细!

2024年福建三支一扶报名已经开始&#xff0c;请注意时间&#xff01; ⚠2024年福建省省级“三支一扶”计划招募岗位1070个 报名时间&#xff1a;4月1日8:00至4月17日17:00 审查考核&#xff1a;4月18日至5月10日 确定派遣人员&#xff1a;5月11日至5月31日 报名入口&#xff1…

Day66-企业级防火墙iptables精讲2

Day66-企业级防火墙iptables精讲2 1. iptables项目案例2&#xff1a;局域网共享上网&#xff1a;2. iptables项目案例3&#xff1a;外网IP的端口映射到内网IP的端口3. 老男孩教育iptables项目案例4&#xff1a;IP一对一映射&#xff08;DMZ&#xff09;4. 老男孩教育iptables项…

jdbc连SQL server,显示1433端口连接失败解决方法

Exception in thread "main" com.microsoft.sqlserver.jdbc.SQLServerException: 通过端口 1433 连接到主机 localhost 的 TCP/IP 连接失败。错误:“connect timed out。请验证连接属性。确保 SQL Server 的实例正在主机上运行&#xff0c;且在此端口接受 TCP/IP 连接…

在project模式下自定义Implementation Strategies

Implementation Settings定义了默认选项&#xff0c;当要定义新的implementation runs时会使用这些选项&#xff0c;选项的值可以在Vivado IDE中进行配置。 图1展示了“Settings”对话框中的“implementation runs”对话框。要从Vivado IDE打开此对话框&#xff0c;请从主菜单中…

文件管理--fscanf,fread,fwrite和fprintf

fprintf函数&#xff1a;对于fprintf函数&#xff0c;它和printf一样&#xff0c;但是它的表达式为&#xff1a;int fprintf ( FILE * stream, const char * format, ... );和printf的很相似&#xff0c;但有不一样。它是格式化输出函数&#xff0c;代码为&#xff1a; #includ…

2024年清明节安装matlab 2024a

下载安装离线支持包SupportSoftwareDownloader_R2024a_win64&#xff0c;地址https://ww2.mathworks.cn/support/install/support-software-downloader.html&#xff0c;运行软件&#xff08;自解压运行&#xff09;&#xff0c;登录账号&#xff08;需要提前在官网注册&#x…

Ubuntu部署BOA服务器

BOA服务器概述 BOA是一款非常小巧的Web服务器&#xff0c;源代码开放、性能优秀、支持CGI通用网关接口技术&#xff0c;特别适合用在嵌入式系统中。 BOA服务器主要功能是在互联嵌入式设备之间进行信息交互&#xff0c;达到通用网络对嵌入式设备进行监控&#xff0c;并将反馈信…

MPLS-基础、LSR、LSP、标签、体系结构

MPLS技术 MPLS基础 MPLS&#xff1a;转发数据时&#xff0c;只在网络边缘分析IP报文头&#xff0c;不在每一跳都分析&#xff0c;节约了转发时间。 MPLS&#xff1a;Multiprotocol Label Switching&#xff0c;多协议标签交换骨干网技术。主要应用&#xff1a;VPN、流量工程…

基于wordcloud、matplotlib等对某东评论数据情感分析-Python数据分析项目

基于wordcloud、matplotlib等对某东评论数据情感分析 文章目录 基于wordcloud、matplotlib等对某东评论数据情感分析1 数据导入及预处理1.1 数据导入1.2 数据描述1.3 数据预处理 2 情感分析2.1 情感分析2.2 情感分直方图2.3 词云图2.4 关键词提取 3 积极评论与消极评论3.1 积极…

【番外篇1】统计学+spss-t检验

目录 t检验 独立样本 t 检验 p值 spss如何分析独立样本t检验 配对样本t检验 单样本t检验 t检验 当我们想要比较两组数据&#xff08;比如两组学生的成绩&#xff09;是否真的有显著差异时&#xff0c;就可以使用 t 检验。 t 检验告诉我们&#xff0c;这种差异是不是真的…

深入解析Hadoop生态核心组件:HDFS、MapReduce和YARN

这里写目录标题 01HDFS02Yarn03Hive04HBase1&#xff0e;特点2&#xff0e;存储 05Spark及Spark Streaming关于作者&#xff1a;推荐理由&#xff1a;作者直播推荐&#xff1a; 一篇讲明白 Hadoop 生态的三大部件 进入大数据阶段就意味着进入NoSQL阶段&#xff0c;更多的是面向…

@Order和@DependsOn的区别

这里写自定义目录标题 一、区别二、demo演示1、Order2、DependsOn 一、区别 Order&#xff1a;改变Bean注入的顺序DependsOn&#xff1a;改变Bean创建的顺序 二、demo演示 1、Order 类 A B 都实现了接口 I &#xff0c;且 A B都由Spring容器创建并且管理 public class A im…