字符串函数和内存函数详解(2)

2023-09-20 18:43:50

🐵本文会将会对剩余的字符串库函数内存函数进行讲解

1.strstr📚

1.1函数用法✏️

strstr函数原型:

strstr用于在字符串中找子串,strstr会返回str1中出现str2的起始地址,如果在str1中没有找到str2,则返回空指针

1.2具体实现🖊️

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

int main()
{
	char arr1[] = "i like chess";
	char arr2[] = "like";

	char* ret = strstr(arr1, arr2); 
	if (ret != NULL)
	{
		printf("%s", ret);
	}
	else
	{
		printf("没找到");
	}

	return 0;
}

strstr会返回arr1中第一次出现arr2的地址,这里就是'l'的地址,将其传给指针ret,在打印时会从'l'处依次向后打印,结果为like chess;如果arr2是字符串"abc"的话,strstr就会返回空指针,打印结果为没找到

1.3模拟实现✍🏻

让str1和str2从第一个地址开始比较,如果相等那str1和str2都向后走一个字节,如果str1和str2一直相等,就应该返回str1中出现str2的起始位置,所以这里应该再来一个指针p用来保存str1和str2比较时str1的起始位置

如果str1和str2进行比较时遇到不相等的字符,就说明从p位置处开始不可能找到str2,所以应先让p向后走一个字节,再让str1回到p处,比如上图中,str1和str2从起始位置开始比较,'a'和'b'不相等,那就说明从p位置处开始不可能出现str2,那么p应该向后走一个字节,再让str1指向p所指的地址;str2应该回到它的起始地址,这里应再定义两个个指针s1,s2,让str1和str2不变,让s1和s2进行比较

总结:如果相等s1和s2一直想后走,当s2遇到'\0'时说明str1里有str2,返回p,如果不相等,p向后走一个字节,s1指向p,s2指向str2,直到相等返回p

代码:

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

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	char* s1 = str1;
	char* s2 = str2;
	char* p = str1;
	while (*p)
	{
		if (*s1 == *s2)
		{
			s1++;
			s2++;
			if (*s2 == '\0')
			{
				return p;
			}
		}
		else
		{
			s2 = str2;
			p++;
			s1 = p;
		}
	}
	return NULL;
}

int main()
{
	char arr1[] = "abbcde";
	char arr2[] = "bcd";
	char* ret = my_strstr(arr1, arr2);
	if (ret != NULL)
	{
		printf("%s\n", ret);
	}
	else
	{
		printf("没找到\n");
	}
	return 0;
}

2.strtok📚

2.1函数用法✏️

strtok函数原型:

strtok用于分割字符串,delimiters指定一个字符串,该字符串中包含分割str的分隔符,而str也指定一个字符串,在该字符串中,包含一个或多个分隔符分割的标记

strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针
strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串
中的位置
。比如:str指定的字符串是12345@qq.com,分隔符为@和.,第一个参数不为NULL,所以strtok会将第一个标记@改为\0,并记住这个位置,在第二次使用strtok时,可以将第一参数设为NULL,这样就会从上一次保存的位置开始找第二个标记
strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标

如果字符串中不存在更多的标记,则返回 NULL 指针。
由于strtok函数回改变源字符串,所以在使用strtok函数前,会临时拷贝一份源字符串,对拷贝后的字符串使用strtok函数

2.2具体实现🖊️

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

int main()
{
	char arr[] = "1234@qq.com";
	char buf[20] = { 0 };
	strcpy(buf, arr);
	char* p = "@.";
	char* ret = NULL;
	for (ret = strtok(buf, p); ret != NULL; ret = strtok(NULL, p))
	{
		printf("%s\n", ret);
	}

	return 0;
}

for循环的初始化部分只会进行一次,这样第一次使用strtok时第一个参数是buf,之后第一个参数都是NULL,这样就能分割整个字符串

打印结果:

3.strerror📚

3.1函数用法✏️

strerror函数原型:

strerror用于返回错误码所对应的错误信息

3.2具体实现🖊️

sterror函数通常是在出现错误码时使用该函数查看错误码所对应的错误信息,并不会特意使用,这里只是举个例子:比如查看1~9所对应的错误信息:

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

int main()
{
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		char*ret = strerror(i);
		printf("%d: %s\n", i, ret);
	}
	return 0;
}

以下是打印结果:

------内存函数------

接下来会讲解memcpy,memmove,memcmp函数,字符串函数比如strcmp只能比较两个字符串,strcpy也只能拷贝字符串,但是在内存中的数据并非只有字符串,所以引入内存函数可以操作任意类型的数据

4.memcpy📚

4.1函数用法✏️

memcpy函数原型:

用于拷贝内存数据,destination指定目标字符串,source指定源字符串,num是要拷贝的字节数

4.2具体实现🖊️

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

int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[10] = { 0 };
	size_t num = 20; //将5个数全部拷贝到arr2,所以是20个字节
	memcpy(arr2, arr1, num);

	return 0;
}

通过调试观察,拷贝前:

拷贝后:

4.3模拟实现✍🏻

由于memcpy再拷贝时是一个字节一个字节拷贝的,所以应将des和src强转为字符指针,每完成一次拷贝就再强转为字符指针后+1,这样就能达到一个字节一个字节拷贝的目的

#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* des, const void* src, size_t n)
{
	assert(des && src);
	void* ret = des;
	while (n--)
	{
		*(char*)des = *(char*)src;
		des = (char*)des + 1;
		src = (char*)src + 1;
	}
	return ret;
}

5.memmove📚

5.1函数用法✏️

memmove函数原型:

可以发现memmove和memcpy的函数原型一致,其实memmove和memcpy用法基本一样,差别就是当源空间和目标空间重叠时可以使用memmove;

比如说一个整形数组int arr[] = {1,2,3,4,5,6,7,8,9};将3~7的位置拷贝到1~5的位置时可以memmove

5.2具体实现🖊️

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

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8 };
	size_t num = 16;
	memmove(arr + 2, arr, num);

	return 0;
}

通过调试观察,拷贝前:

拷贝后:

5.3模拟实现✍🏻

如果按照memcpy函数模拟实现的方法来模拟实现的话,无法达到预期目的(假如是将2~5拷贝到4~7的位置处):

当将2和3分别拷贝到4和5的位置后,在接下来要将4拷贝到6的位置时就已经无法达到目的了,因为此时4的位置已经被2拷贝过,再去拷贝到6的位置时,实际上是将2拷贝6的位置,那么整个拷贝操作结束后该数组会变为1 2 3 2 3 2 3 8,在这种des在src之后的情况下可以将src从后往前拷贝,5->7,4->6,3->5,2->4,这样就能达到拷贝的目的

反之当des在src之前时,应将src从前往后拷贝,这种情况下的模拟实现和memcpy一致

#include<stdio.h>
#include<assert.h>
void* my_memmove(void* des, const void* src, size_t n)
{
	assert(des && src);
	void* ret = des;
	if (des > src)
	{
		while (n--)
		{
			*((char*)des + n) = *((char*)src + n);
		}
	}
	else
	{
		while (n--)
		{
			*(char*)des = *(char*)src;
			des = (char*)des + 1;
			src = (char*)src + 1;
		}
	}
	return ret;
}

6.memcmp📚

6.1函数用法✏️

memcmp函数原型:

用于比较两个内存数据,ptr1和ptr2分别指定两个内存数据,num代表要比较的前num个字节

6.2具体实现🖊️

注意:这里比较的是前num个字节

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

int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,5,6 };
	size_t num = 0;
	scanf("%zd", &num);

	int cmp = memcmp(arr1, arr2, num);
	if (cmp > 0)
	{
		printf("arr1 > arr2\n");
	}
	else
	{
		printf("arr1 <= arr2\n");
	}

	return 0;
}

7.memset📚

7.1函数用法✏️

memset函数原型:

memset用来设置内存的,ptr指向一个内存数据,value是要设置的值,num是要设置的字节数

7.2具体实现🖊️

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

int main()
{
	char arr[] = "hello world";
	memset(arr + 6, 'x', 5);
	printf("%s", arr); 打印结果:hello xxxxx

	int arr[10] = { 0 };
	memset(arr, 1, 40); //这里并不会将arr里面变为10个1,因为他是一个字节一个字节设置的
//所以它将每个字节设为了1,那么就变成了10个非常大的数
//综上所述,memset适合于字符数组,或者将整形数组全部设为0
	return 0;
}

🙉至此,字符串函数和内存函数全部讲解完毕!后续将会讲解结构体、联合、枚举相关内容

更多推荐

【云原生之Docker实战】使用Docker部署flatnotes笔记工具

【云原生之Docker实战】使用Docker部署flatnotes笔记工具一、flatnotes介绍1.1flatnotes简介1.2flatnotes特点二、本地环境介绍2.1本地环境规划2.2本次实践介绍三、本地环境检查3.1检查Docker服务状态3.2检查Docker版本3.3检查dockercompose版本

036:vue导出页面生成pdf文件

第036个查看专栏目录:VUE------elementUI专栏目标在vue和elementUI联合技术栈的操控下,本专栏提供行之有效的源代码示例和信息点介绍,做到灵活运用。(1)提供vue2的一些基本操作:安装、引用,模板使用,computed,watch,生命周期(beforeCreate,created,befo

Hadoop sqoop

0目录1.安装sqoop2.补充sqoop流程1.安装sqoop解压、改名[root@kb129install]#tar-xvf./sqoop-1.4.7.tar.gz-C/opt/soft/[root@kb129soft]#mvsqoop-1.4.7/sqoop147拷贝配置文件[root@kb129conf]#pw

[k8s] kubectl port-forward 和kubectl expose的区别

kubectlport-forward和kubectlexpose是Kubernetes命令行工具kubectl提供的两种不同方式来公开服务。kubectlport-forwardkubectlport-forward命令用于在本地主机和集群内部的Pod之间建立一个临时的端口转发通道。该命令将本地机器上的一个端口绑定到

DVWA靶机,通过XSS盗取cookie登录

文章目录一发现XSS漏洞1.1登录DVWA,找到XSS(DOM)模块,测试XSS是否存在。1.2在选项的传参后面加入一串js代码,也就是可以XSS弹出的代码。payload如下:二、盗取cookie2.1在XSS平台上搭建一个测试项目。这里用的是免费的XSS平台:选择【我的项目】->【创建】,创建一个测试项目:2.2配

Java高级-注解

注解1.介绍2.元注解3.注解的解析4.注解的应用场景1.介绍注解Annotation就是Java代码里的特殊标记,作用是让其他程序根据注解信息来决定什么是执行该程序注解:注解可以在类上、构造器上、方法上、成员变量上、参数上等位置自定义注解/***自定义注解*/public@interfaceMyTest1{Strin

IntelliJ IDEA 2023.2 最新版如何激活?IntelliJ IDEA 2023.2最新版激活方法及验证ja-netfilter配置成功提示

🌷🍁博主猫头虎带您GotoNewWorld.✨🍁🦄博客首页——猫头虎的博客🎐🐳《面试题大全专栏》文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺🌊《IDEA开发秘籍专栏》学会IDEA常用操作,工作效率翻倍~💐🌊《100天精通Golang(基础入门篇)》学会Golang语言,畅玩云原生,走遍大

SpringBoot +MyBatis批量插入数据

💍背景介绍在最近的开发过程中,遇到了往数据库中表中插入大量的数据。有一个全国银行各分行的信息,共计148032条数据文件有8.45MB,因为考虑到数据量比较大,就想着导入到MySQL看一看需要多长时间。💍方案一:用for语句循环插入(不推荐)使用for循环语句将,将数据一条条插入。insertintot_bankv

【LeetCode-面试经典150题-day25】

目录530.二叉搜索树的最小绝对差230.二叉搜索树中第K小的元素98.验证二叉搜索树530.二叉搜索树的最小绝对差题意:给你一个二叉搜索树的根节点root,返回树中任意两不同节点值之间的最小差值。差值是一个正数,其数值等于两值之差的绝对值。树中节点的数目范围是[2,100]0<=Node.val<=105【输入样例】

React+Typescript项目环境中搭建并使用redux环境

前几篇文章我们的项目已经开始功能渐渐完善了那么我们来说最后一个点redux这个并不需要我们多努力其实官方文档给到已经算是很全面了我们可以直接访问地址TypeScript中文手册中文手册和官方是一样的而且对我们非常友好我们会在左侧导航栏中找到一个React点进去进入之后一直往下翻我们就可以看到Redux部分我们直接用他这

9.子数组统计问题

文章目录子数组计数问题[560.和为K的子数组](https://leetcode.cn/problems/subarray-sum-equals-k/)[974.和可被K整除的子数组](https://leetcode.cn/problems/subarray-sums-divisible-by-k/)[523.连续

热文推荐