• 【C语言】字符串函数及模拟实现strlen&&strcpy&&strcat&&strcmp


    ✨作者:@平凡的人1

    ✨专栏:《C语言从0到1》

    ✨一句话:凡是过往,皆为序章

    ✨说明: 过去无可挽回, 未来可以改变


    🔴前言

    • 我们知道,在C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的(不像其他语言直接String即可解决),在C语言中字符串通常放在常量字符串中或者字符数组中。

    • 字符串常量适用于那些对它不做修改的字符串函数。

    我们这次主要介绍的就是一些字符串函数。

    • 关于学习一些陌生的函数:我们可以通过cplusplus.com进行了解学习,知道参数所代表的含义以及实现的功能是上面,这可以让我们事半功倍!对于英文不理解的话,我们可以翻译成自己熟悉的中文啦。💗
    • 一定要自己多动手去实现代码哦,也不要一口气吃成胖子💖

    🟠求字符串长度——strlen()

    image-20220704221502825

    关于**strlen()**我们需要知道几个点:

    size_t strlen ( const char * str );//返回无符号整型
    
    • 1
    1. 字符串已经 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包 含 ‘\0’ )。
    2. 参数指向的字符串必须要以 ‘\0’ 结束。
    3. 注意函数的返回值为size_t,是无符号的( 易错 )
    4. 学会strlen函数的模拟实现

    对于第3点我们怎么去理解❓我们可以来举个例子:

    #include <stdio.h>
    #include <string.h>
    int main()
    {
    	//返回无符号整型
    	if (strlen("abc") - strlen("abcdef") > 0)
    	{
    		printf(">\n");
    	}
    	else
    	{
    		printf("<\n");
    	}
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    输出结果是什么

    是:>

    因为strlen()返回无符号整型,虽然3-6<0,但是对于无符号的数来说,怎么可能有负数呢?所以结果肯定是>号

    我们可以来测试运行一下结果:

    image-20220704174629850

    本篇博客主要的内容就是函数的模拟实现

    下面我们介绍strlen()的三种模拟实现方法👇:

    1.计数器方法

    直接定义一个变量去统计字符串的长度:

    #include <stdio.h>
    #include <string.h>
    #include <assert.h>
    //计数器方法
    size_t my_strlen(const char*str)
    {
    	int count = 0;
    	assert(str);
    	while (*str != '\0')
    	{
    		count++;
    		str++;
    	}
    	return count;
    }
    int main()
    {
    	char arr[] = "abcdef";
    	size_t n = my_strlen(arr);
    	printf("%u\n", n);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    同时,我们需要严谨一些,注意一些细节📝:

    1. 第一个就是const修饰,我们只需要去访问,不需要进行修改操作
    2. 第二个就是assert断言,如果传过来的是空的呢?这就是assert的用处了
    3. 第三个就是返回值size_t无符号

    2.指针-指针方法

    #include <stdio.h>
    #include <string.h>
    #include <assert.h>
    size_t my_strlen(const char* str)
    {
    	assert(str);
    	char* ret = str;
    	while (*str != '\0')
    	{
    		str++;
    	}
    	return str - ret;
    }
    int main()
    {
    	char arr[] = "abcdef";
    	size_t n = my_strlen(arr);
    	printf("%u\n", n);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    指针减去指针就是元素的个数,所以我们记录刚开始的地址,由最后一个指针地址减去刚开始记录的指针地址就可以得到长度了!

    3.递归

    找出递归条件可以完美模拟实现strlen()。下面进行代码实现:

    #include <stdio.h>
    #include <string.h>
    #include <assert.h>
    size_t my_strlen(const char* str)
    {
    	assert(str);
    	if (*str == '\0')
    	{
    		return 0;
    	}
    	return 1 + my_strlen(str + 1);
    }
    
    int main()
    {
    	char arr[] = "abcdef";
    	size_t n = my_strlen(arr);
    	printf("%u\n", n);
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    测试运行:image-20220704202600685

    查看源图像


    🟡长度不受限制的字符串函数

    strcpy

    image-20220704221541436

    对于strcpy(),我们要知道几个点:

    char* strcpy(char * destination, const char * source );
    
    • 1
    1. Copies the C string pointed by source into the array pointed by destination, including the terminating null character (and stopping at that point). 简单理解就是字符串的拷贝,不过拷贝要注意下面几个点:
    2. 源字符串必须以 ‘\0’ 结束。
    3. 会将源字符串中的 ‘\0’ 拷贝到目标空间。
    4. 目标空间必须足够大,以确保能存放源字符串。
    5. 目标空间必须可变。 学会模拟实现。

    我觉得这个函数最重要的一点是:第一个参数是目的地字符串,第二个参数是源来的字符串。第二个参数是不可改变的,可以用const来修饰。这点比较重要。

    下面我们来对其进行模拟实现:

    //模拟实现
    #include <stdio.h>
    char* my_strcpy(char* dest, const char* src)
    {
    	assert(dest);
        assert(src);
    	char* ret = dest;
    	while (*src)
    	{
    		*dest++ = *src++;
    	}
    	*dest = *src;
    	return ret;
    }
    int main()
    {
    	char arr1[] = "abcdef";
    	char arr2[20] = { 0 };
    	my_strcpy(arr2, arr1);
    	printf("%s\n", arr2);
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    对于这次的模拟实现我们可以进行优化,使之看起来更加简化

    #include <stdio.h>
    char* my_strcpy(char* dest, char* src)
    {
    	assert(dest && src);
    	char* ret = dest;
    	while (*dest++ = *src++)
    	{
    		;
    	}
    	return ret;
    }
    
    
    int main()
    {
    	char arr1[] = "abcdef";
    	char arr2[20] = { 0 };
    	my_strcpy(arr2, arr1);
    	printf("%s\n", arr2);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    测试运行结果:image-20220704205525596

    strcat

    image-20220704221611657

    char * strcat ( char * destination, const char * source );
    
    • 1
    1. Appends a copy of the source string to the destination string. The terminating null character in destination is overwritten by the first character of source, and a null-character is included at the end of the new string formed by the concatenation of both in destination. 简单理解,就是字符串的追加。
    2. 源字符串必须以 ‘\0’ 结束。
    3. 目标空间必须有足够的大,能容纳下源字符串的内容。
    4. 目标空间必须可修改。 字符串自己给自己追加,如何?

    有了上面的知识点之后,对于strcat我们是很好理解的,下面我们先来进行模拟实现:

    #include <stdio.h>
    //字符串追加
    //字符串追加
    char* my_strcat(char* dest, const char* src)
    {
    	assert(dest && src);
    	int ret = dest;
    	while (*dest != '\0')
    	{
    		dest++;
    	}
    	while (*dest++ = *src++)
    	{
    		;
    	}
    	return ret;
    }
    int main()
    {
    	char arr1[20] = "hello ";
    	strcat(arr1, "world");
    	//my_strcat(arr1, "world");
    	//自己给自己追加?
    	//my_strcat(arr1, arr1);//这是错误的,破坏了本身自己。\0不见了
    	printf("%s\n", arr1);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    测试运行:image-20220704210433269

    这里有人会问了:自己给自己追加呢?想想看,这是错误的做法,因为你会发现,追加自己的过程中本身自己发生了变化,'\0’被覆盖了,根本无法实现。无论是我们自己模拟实现的或者库函数自己的,都没法自己追加自己。

    strcmp

    image-20220704221647157

    int strcmp ( const char * str1, const char * str2 );
    
    • 1
    1. This function starts comparing the first character of each string. If they are equal to each other, it continues with the following pairs until the characters differ or until a terminating null-character is reached. 简单来说,就是进行字符串的比较。
    2. 标准规定: 第一个字符串大于第二个字符串,则返回大于0的数字
    3. 第一个字符串等于第二个字符串,则返回0
    4. 第一个字符串小于第二个字符串,则返回小于0的数字
    5. 那么如何判断两个字符串?比较的是对应的ASCII码值

    先来简单理解一下库函数把:

    #include <stdio.h>
    int main()
    {
    	int ret = strcmp("abbb", "abq");
    	printf("%d", ret);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    将会得到:image-20220704222653062

    因为第三位b小于q的ASCII码值,自然返回-1。这就是strcmp的基本原理。

    不过这里我们要对其进行模拟实现,怎么模拟实现呢❓

    #include <stdio.h>
    int my_strcmp(const char* s1, const char* s2)
    {
    	while (*s1 == *s2)
    	{
    		if (*s1 == '\0')
    		{
    			return 0;
    		}
    		s1++;
    		s2++;
    	}
    	if (*s1 > *s2)
    	{
    		return 1;
    	}
    	else 
    	{
    		return -1;
    	}
    }
    
    int main()
    {
    	char* p = "abcdef";
    	char* q = "abbb";
    	int ret = my_strcmp(p, q);
    	if (ret>0)
    	{
    		printf("p>q");
    	}
    	else if(ret <0)
    	{
    		printf("p<q");
    	}
    	else
    	{
    		printf("p=q");
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    测试运行结果:image-20220704224809828

    这样就完了吗?查看源图像并没有,我们可以对模拟实现的代码进行优化简洁一些:

    //优化模拟实现
    #include <stdio.h>
    #include <assert.h>
    int my_strcmp(const char* s1, const char* s2)
    {
    	assert(s1 && s2);
    	while (*s1 == *s2)
    	{
    		if (*s1 == '\0')
    		{
    			return 0;
    		}
    		s1++;
    		s2++;
    	}
    	return *s1 - *s2;
    }
    
    int main()
    {
    	char* p = "abcdef";
    	char* q = "abbb";
    	int ret = my_strcmp(p, q);
    	if (ret>0)
    	{
    		printf("p>q");
    	}
    	else if(ret <0)
    	{
    		printf("p<q");
    	}
    	else
    	{
    		printf("p=q");
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    注意到:image-20220704224552653

    并没有具体要求<0的数是多少,或者说>0的数是多少,我们直接去相减即可。

    测试运行一下代码:image-20220704224706998


    🟢总结

    关于求字符串长度以及长度不受限制的字符串函数的相关介绍及其模拟实现就到这里结束了,然后考虑到实际情况,我们一次性如果学太多库函数的话反而不利于形成记忆,对函数不能熟练的掌握,本篇博客就先介绍这4个函数的实现,其余的留在下次!

    同时记得自己多总结一下,多动手去敲代码,不要自己认为看得懂就会了,实际操作起来是跟自己的想法是不一样的,希望大家能够多动手去实践一下!🌹


  • 相关阅读:
    EXCEL如何把一个单元格内的文本和数字分开?例如:龚龚15565 = 龚龚 15565
    【20年扬大真题】编写对数组求逆的递归算法
    安卓绘制原理之 MeasureCache优化了什么?
    使用CIntHeap 二叉堆进行A*路径搜索代码学习
    Mybatis generator实战:自动生成POJO类完整解决方案
    SPA项目开发--表单校验+增删改功能
    Linux文本处理工具awk用法总结
    Linux 下升级 OpenSSH 到 9.x 版本对 scp 影响
    Modbus调试软件使用教程
    读书记:认知觉醒(一)大脑、焦虑、耐心
  • 原文地址:https://blog.csdn.net/weixin_60478154/article/details/125610500