对字符串和内存函数的认识(1)

        我们平常在写代码时,有时明明只是想简单的将数组复制,将字符串拷贝一份等等,却需要重新写一个函数来实现。然而,大部分我们认为的简单却又复杂的操作都有所对应的库函数,今天,我们就来了解一些库函数并且模拟实现它们。

目录

目录

函数介绍

strlen

strlen的简单认识

模拟实现strlen 

计数器法

递归法

 指针减指针

strcpy 

strcpy的简单认识 

模拟实现strcpy 

strcat 

strcat的简单认识 

模拟实现strcat 

注意事项 

strcmp 

strcmp的简单认识 

 模拟实现strcmp

strncpy 

strncpy的简单认识 

模拟实现strncpy 

strncat 

对strncat的简单认识 

模拟实现strncat 

strncmp 

 模拟实现strncmp

小结 


函数介绍

strlen

strlen的简单认识

         strlen,从单词来看,就是The length of a C string,字符串长度,我们可以推测:这个函数是用来求字符串长度的。我们从strlen - C++ Reference (cplusplus.com)

        我们可以看到,它确实是用来求字符串长度的,我们看到它的参数是 const char * str,一个被const修饰的字符指针,所以我们只需要将字符指针传给该函数即可。那么,我们接下来就来简单的应用它(注意:strlen的返回值是size_t类型的,那么我们就用size_t来进行接收,相应的打印时需要用%zd来进行打印):

#include<string.h>
int main()
{
	const char* str = "abcdef";
	size_t len1 = strlen("abcdef");//注意:这里的"abcdef"是地址而不是字符串,我们从str的参数类型就可以看出
	size_t len2 = strlen(str);//这里我们将地址传给strlen
	printf("%zd\n", len1);
	printf("%zd\n", len2);
	return 0;
}

运行结果如下:

        我们在上面的代码中使用了size_t,那么,什么是size_t?size_t又是怎么来运用的呢?

既然strlen的返回值是size_t,那么我们就让两个size_t的值相减来进行判断:

int main()
{
	size_t ret = strlen("abc") - strlen("abcdef");
	if (ret > 0)
		printf(">=\n");//如果>0,返回>=
	if (ret < 0)
		printf("<");//如果<0,返回<
	return 0;
}

        我们从数学的角度出发,abc的长度明显是小于abcdef的,那么就应该打印<。然而,打印结果是:

        这种情况我们就只能想到一种可能,那就是size_t是无符号整型,无符号整型与无符号整型相减还是无符号整型,那么就会打印出>=

在了解如何使用strlen后,我们来模拟实现它。

模拟实现strlen 

        那么,如何实现strlen呢?这里我们用3种方法来对它进行实现:

计数器法

         我们知道,字符串是以'\0'结束的,那么我们就可以创建一个指针指向字符串的起始位置,创建一个计数变量,让指针依次移动,同时让计数变量++,到指针指向'\0'时停止计数,代码如下:

int my_strlen(char* str)
{
    assert(str);//这里我们进行断言,判断是否为空
	int count = 0;//创建计数器
	while (*str)//当str指向'\0'时,退出循环
	{
		count++;
		str++;
	}
	return count;
}

int main()
{
	char arr[] = "abcdef";
	int ret = my_strlen(arr);
	printf("该字符串长度为:%d\n", ret);
	return 0;
}

运行结果如下:

递归法

        我们也可以用递归的方法来实现,其实递归也是一样的,让函数不断递归,在指向'\0'的时候停止,在函数进行递归时,每次递归数+1,从而完成让指针移动和计数,代码如下:

#include<assert.h>
int my_strlen(char* str)
{
    assert(str);//进行断言,判断是否为空
	if (*str == '\0')
		return 0;
	return my_strlen(str + 1) + 1;//我们让函数在递归的同时不断+1,直到'\0'。
}

int main()
{
	char arr[] = "abcdef";
	int ret = my_strlen(arr);
	printf("该字符串长度为:%d\n", ret);
	return 0;
}

 运行结果如下:

 指针减指针

        什么是指针减指针呢?其实就是地址减地址(狗头),指针减指针所得到的是两个地址之间元素的个数。因此,我们需要得到指向'\0'的地址,在让它与首地址相减即可,代码如下:

int my_strlen(char* str)
{
	char* tmp = str;
	while (*str)
	{
		str++;
	}
	int num = (int)(str - tmp);//我们需要得到int类型,因此强制转换成int类型
	return num;
}

int main()
{
	char arr[] = "abcdef";
	int ret = my_strlen(arr);
	printf("该字符串长度为:%d\n", ret);
	return 0;
}

运行结果如下:

strcpy 

strcpy的简单认识 

         我们很容易就看出来,strcpy就是对字符串进行拷贝,那么应该如何来使用呢,我们先对其进行了解:

        从对其参数的观察,我们可以发现,strcpy是用字符指针将源头的参数拷贝到目的地参数。那么,我们就来简单实现:

#include<string.h>
int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "hello world!";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);//在拷贝完成后对目的地进行打印
	return 0;
}

运行结果如下:

        但是,我们在使用strcpy时,需要注意以下几点:

        在对strcpy进行使用时,我们注意这几点:
        1.源字符串必须以 '\0' 结束。
        2.会将源字符串中的 '\0' 拷贝到目标空间。
        3.目标空间必须足够大,以确保能存放源字符串。
        4.目标空间必须可变。

        也就是说,我们使用strcpy进行拷贝时,会将'\0'也一并拷贝过去,倘若没有'\0',会发生越界,那我们来验证:

int main()
{
	char arr1[20] = "xxxxxxxxxxxxxxxxxx";
	char arr2[] = "hello world!";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

        进行调试:

        这里我们很清楚的看到,strcpy是会将'\0'也一并拷贝过去的。

        了解完strcpy的用法后,我们对strcpy进行模拟实现。

模拟实现strcpy 

        模拟实现strcpy,我们只需要将源头的指针解引用后赋给目的地 ,由于strcpy的返回值是char*,我们在模拟实现时需要先另存一份目的地的地址:

#include<assert.h>
char* my_strcpy(char* dest, const char* src)//源头不可修改
{
	assert(dest && src);
	char* tmp = dest;
	while (*dest++ = *src++)//不止一次拷贝,因此使用循环
	{
		;//这里我们需要将'\0'也一并拷贝过去,所有这里后置++。
	}
	return tmp;
}

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

运行结果如下:

strcat 

strcat的简单认识 

         strcat是对字符串进行追加,那么问题来了,什么是对字符串进行追加?一个简单的例子:有一个字符串变量a1="abc";和另一个字符串变量a2="def";在进行追加之后,我们可以得到"abcedf",这就是字符串追加。那么,我们应该如何对其进行使用呢?

        我们从其参数可以知道,strcat是将源头的参数追加到目的地的后方,那么我们简单应用:

int main()
{
	char arr1[20] = "abc";
	char arr2[] = "def";
	strcat(arr1, arr2);
	printf("%s\n", arr1);
    return 0;
}

运行结果如下:

        于是我们知道,使用strcat会将目的地的'\0'给覆盖,那么strcat在追加时会将源头的'\0'也追加过去吗?我们进行验证:

int main()
{
	char arr1[20] = "abc\0xxxxx";
	char arr2[] = "def";
	strcat(arr1, arr2);
	printf("%s\n", arr1);
}

        我们进行调试:

        我们得知,strcat会将'\0'也一并追加过去。

        那么,我们接下来对其进行模拟实现。

模拟实现strcat 

        如何模拟实现strcat呢?那么我们首先需要找到目的地的'\0'的位置,然后再将源头的参数一一追加过去,strcat的返回值也是char*类型,那么我们同样再保存一份目的地的地址,代码如下:

#include<assert.h>
char* my_strcat(char* dest,const char* src)
{
	assert(dest && src);//断言,看查是否为空
	char* tmp = dest;
	//找到目的地的末尾
	while (*dest != '\0')
	{
		dest++;
	}
	//进行追加
	//在这时我们进行追加,我们会发现接下来的过程和strcpy是一样的,代码如下:
	while (*dest++ = *src++)
	{
		;
	}
	return tmp;//返回目的地的地址
}

int main()
{
	char arr1[20] = "abc";
	char arr2[] = "def";
	my_strcat(arr1, arr2);
	printf("%s\n", arr1);
}

 运行结果如下:

注意事项 

         我们在使用strcat时,需要注意:

        1.源字符串必须以 '\0' 结束。
        2.目标空间必须有足够的大,能容纳下源字符串的内容。
        3.目标空间必须可修改。

        那么,strcat可以自己给自己追加吗?我们进行实验:

#include<assert.h>
char* my_strcat(char* dest,const char* src)
	assert(dest && src);
	char* tmp = dest;
	//找到目的地的末尾
	while (*dest != '\0')
	{
		dest++;
	}
	//进行追加
	while (*dest++ = *src++)
	{
		;
	}
	return tmp;//返回目的地的地址
}

int main()
{
	char arr1[20] = "abc";
	char arr2[] = "def";
	my_strcat(arr1, arr1);
	printf("%s\n", arr1);
}

        我们运行后,程序崩溃,从而得出结论:我们所写的模拟strcat是不能对自己进行追加的,为什么呢?我们画图分析:

        但是,库函数的strcat是可以实现对自己的追加的,说明库函数进行了优化。

strcmp 

strcmp的简单认识 

        strcmp我们在之前使用比较频繁,我们知道,它是进行字符串比较的:

        我们从上图中得知,strcmp不是来比较长度的,它是比较对应元素的大小的,这里举一个例子:

       而strcmp的返回值是int类型,这是为什么呢?那么它该如何使用?我们接着往下看:

        从这里我们可以知道,strcmp是在对元素进行比较后通过返回值与0进行比较来进行判断。

        那么我们接下来就对其进行简单应用:

#include<string.h>

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abg";
	int ret = strcmp(arr1, arr2);
	if (ret > 0)
		printf("arr1>arr2\n");
	else if (ret == 0)
		printf("arr1=arr2\n");
	else
		printf("arr1<arr2\n");
	return 0;
}

 运行结果如下:

 模拟实现strcmp

        模拟实现strcmp,我们只需要使用两个指针,让这两个指针在移动的同时对所指元素解引用并进行比较,当一个指针或两个指针同时指向'\0'时,停止移动,返回一个整型进行判断,代码如下:

#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	if (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;
		str1++;
		str2++;
	}
	else if (*str1 > *str2)
		return 1;
	else
		return -1;
}

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abg";
	int ret = my_strcmp(arr1, arr2);
	if (ret > 0)
		printf("arr1>arr2\n");
	else if (ret == 0)
		printf("arr1=arr2\n");
	else
		printf("arr1<arr2\n");
	return 0;
}

 运行结果如下:

        以上的函数都有一种特点:长度都不会受到限制,C语言在提供长度受限制的函数的同时,也给我们提供了一些长度受限制的函数,下面我们来认识几个长度受限制的函数。

strncpy 

strncpy的简单认识 

        strncpy相比于strcpy多了一个字母n,那么它们有何区别呢?我们往下看:

        我们可以看到,strncpy的参数多了一个参数 size_t num ,再结合下文我们了解到,strncpy要将想要拷贝的元素个数传递过去,在源头和目的地函数上,没有多大区别,下面我们使用它:

#include<string.h>
int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "abcdefg";
	strncpy(arr1, arr2, 3);
	printf("%s\n", arr1);
	return 0;
}

运行结果如下:

        那么,strncpy在进行拷贝的时候会将'\0'拷贝过去吗?我们进行验证:

#include<string.h>
int main()
{
	char arr1[20] = "xxxxxxxxxxxxxxxxxx";
	char arr2[] = "abcdefg";
	strncpy(arr1, arr2, 3);
	printf("%s\n", arr1);
	return 0;
}

        进行调试:

        我们可以看到,strncpy是没有将'\0'拷贝过去的,既然没有拷贝'\0',这时我们再想一个问题,如果想要拷贝的个数大于数组的元素个数时,会发生什么?

#include<string.h>
int main()
{
	char arr1[20] = "xxxxxxxxxxxxxxxxxx";
	char arr2[] = "abcd";
	strncpy(arr1, arr2, 6);//将数组的字符改为4个,此处拷贝6个
	printf("%s\n", arr1);
	return 0;
}

        进行调试:

        我们会发现,strncpy会在大于拷贝元素的后面补'\0'。所以strncpy比起strcpy,会相对的安全一点,因为我们在使用strncpy时会考虑拷贝的元素有几个,能否拷贝过去等问题。

        在了解完其用法后,我们来对strncpy进行模拟实现。

模拟实现strncpy 

        我们在上面了解到,strncpy只是在strcpy的基础上增加了想要拷贝的个数num,那么我们只需要将想要拷贝的个数传过去,在大于拷贝元素的后面补'\0'就可以了,代码如下:

#include<assert.h>
char* my_strcpy(char* dest, const char* src, size_t num)//源头不可修改
{
	//这里进行断言
	assert(dest && src);
	char* tmp = dest;//这里将原来的目的地地址进行拷贝,下面的循环会对地址进行修改
	size_t sz = strlen(src);
	if (num <= sz)
	{
		for (size_t i = 0; i < num; i++)//strncpy是不会将'\0'一并拷贝过去的
		{
			*dest = *src;
			dest++;
			src++;
		}
	}
	else if (num > sz)
	{
		size_t n = num;
		num = sz;
		for (size_t i = 0; i < num; i++)
		{
			*dest = *src;
			dest++;
			src++;
		}
		for (size_t j = sz; j < n; j++)
		{
			*dest = '\0';
			dest++;
		}
	}
	return tmp;
}

int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "abcdef";
	size_t num = 0;
	printf("请输入想要拷贝的个数:<");
	scanf("%zd", &num);
	my_strcpy(arr1, arr2, num);
	printf("%s\n", arr1);
	return 0;
}

 运行结果如下:

strncat 

对strncat的简单认识 

         strncatstrcat对比,也是参数上多了size_t num,这意味着我们在追加的时候需要考虑追加几个元素:

        我们可以看到,在除了传递追加的个数以外的使用方法是没有发生改变的,那么我们接下来就对其进行使用:

#include<string.h>
int main()
{
	char arr1[20] = "abc";
	char arr2[] = "defghi";
	strncat(arr1, arr2, 3);
	printf("%s\n", arr1);
	return 0;
}

运行结果如下:

        这时我们就会产生同样的问题,strncat在使用的时候会将'\0'给追加过去吗?我们对代码进行修改和验证:

#include<string.h>
int main()
{
	char arr1[20] = "abc\0xxxxxxxx";
	char arr2[] = "defghi";
	strncat(arr1, arr2, 3);
	printf("%s\n", arr1);
	return 0;
}

        进行调试:

        我们可以清楚的看到,strncat是会将'\0'也一并拷贝过去的,那么,如果我特意把要追加的元素大于数组当中的元素个数会发生什么呢?

#include<string.h>
int main()
{
	char arr1[20] = "abc\0xxxxxxxx";
	char arr2[] = "defghi";
	strncat(arr1, arr2, 10);//我们改为追加10个元素
	printf("%s\n", arr1);
	return 0;
}

        我们进行调试:

运行结果如下:

        我们可以发现,是不会发生什么的,在追加完数组的元素之后,strncat不会对后续做出处理,那么,我们就在此基础上,模拟实现strncat

模拟实现strncat 

        相比于模拟实现strcat,我们额外注意两点:
        1.追加的个数。
        2.如果追加的数超过数组元素个数,不做处理 。

        那么我们就对其进行模拟实现:

#include<assert.h>
char* my_strcat(char* dest, const char* src, size_t num)
{
	assert(dest && src);//断言,看查是否为空
	char* tmp = dest;
	//找到目的地的末尾
	while (*dest != '\0')
	{
		dest++;
	}
	//开始追加,注意追加个数(因为追加会将'\0'也一并拷贝,所以超出的个数就不做处理
	for (size_t i = 0; i < num; i++)
	{
		*dest = *src;
		dest++;
		src++;
	}
	return tmp;//返回目的地的地址
}

int main()
{
	char arr1[20] = "abc";
	char arr2[] = "def";
	size_t num = 0;
	printf("请输入想要追加的个数:<");
	scanf("%zd", &num);
	my_strcat(arr1, arr2, num);
	printf("%s\n", arr1);
}

运行结果如下:

strncmp 

对strncmp的简单认识 

        我们可以看到:

        strncmp,它的参数也是多了size_t num,也就是说,它是根据操作者想要比较的元素个数来进行比较的,于是我们对其进行使用:

#include<string.h>
int main()
{
	char arr1[20] = "abcdef";
	char arr2[] = "abcqw";
	int ret = strncmp(arr1, arr2, 4);
	if (ret > 0)
	{
		printf(">\n");
	}
	else if (ret == 0)
	{
		printf("=\n");
	}
	else
		printf("<\n");
	return 0;
}

运行结果如下:

        那么接下来我们就对strncmp进行模拟实现。

 模拟实现strncmp

        也是一样的,我们只需要在模拟实现strcmp的基础上将元素个数传递过去就可以啦:

#include<assert.h>

int my_strncmp(const char* str1, const char* str2, int num)
{
	assert(str1 && str2);
	int ret = 0;
	if (*str1 == *str2)
	{
		for (int i = 0; i < num-1; i++)
		{
			if (*str1 == '\0')
			{
				ret = 0;
				break;
			}
			str1++;
			str2++;
		}
		if (*str1 > *str2)
			ret = 1;
		else if (*str1 < *str2)
			ret = -1;
	}
	return ret;
}

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abg";
	int num = 0;
	printf("请输入想要比较的元素个数:<");
	scanf("%d", &num);
	int ret = my_strncmp(arr1, arr2, num);
	if (ret > 0)
		printf("arr1>arr2\n");
	else if (ret == 0)
		printf("arr1=arr2\n");
	else
		printf("arr1<arr2\n");
	return 0;
}

运行结果如下:

小结 

        今天我们认识的函数都与字符有关,那么如果我使用整型,浮点型,也有对应的库函数吗?当然是有的,但是今天我们就先认识这么多吧,在计算机知识得海洋里,我们还是得继续加油啊,继续冲锋!好啦,今天先到这里,我们下次再见! 

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值