第二十四章 字符串函数(1)

C语言学习之路

第一章 初识C语言
第二章 变量
第三章 常量
第四章 字符串与转义字符
第五章 数组
第六章 操作符
第七章 指针
第八章 结构体
第九章 控制语句之条件语句
第十章 控制语句之循环语句
第十一章 控制语句之转向语句
第十二章 函数基础
第十三章 函数进阶(一)(嵌套使用与链式访问)
第十四章 函数进阶(二)(函数递归)
第十五章 数组进阶
第十六章 操作符(详解及注意事项)
第十七章 指针进阶(1)
第十八章 指针进阶(2)
第十九章 指针进阶(3)
第二十章 指针进阶(4)
第二十一章 数据的存储(秒懂原、反、补码)
第二十二章 指针和数组笔试题详解(1)
第二十三章 指针和数组笔试题详解(2)
第二十四章 字符串函数(1)



前言

本章将对字符串相关的部分常用的库函数进行剖析、应用、实现。


一、strlen函数

1、函数的用途:

strlen即String Length(字符串的长度)。所以这个库函数是用来求一个字符串的长度的。
在这里插入图片描述
首先,我们来观察一下这个函数的返回值类型,size_t。当我们在VS编译器中将这个返回值类型转到定义的时候,我们会看到:

我们发现,size_t本质上是一个无符号整型,这个无符号整型代表的是字符串长度。这个是很好理解的,因为我们的字符串长度肯定是正数。
然后,我们看一下这个函数的形参列表,const char *str,因为字符串也是一个数组,而数组名就是首元素的地址,而地址所对的指针类型是char。加了const是为了避免我们在该函数体的内部修改了原字符串。
接着,我们了解一下该函数的计算原理,strlen函数会从首元素开始向后遍历,直到遍历到\0的时候才会停止,并且返回这个字符串的长度(不包含\0)。
最后,我们需要注意一些容易忽略的点:

  • 字符串已经 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包
    含 ‘\0’ )。
  • 参数指向的字符串必须要以 ‘\0’ 结束。
  • 注意函数的返回值为size_t,是无符号的( 易错 )。

2、strlen的使用

(1)简单应用:

#include<stdio.h>
#include<string.h>
int main()
{
	char str[]="abcdef";
	int ret=strlen(str);
	printf("字符串的长度为:%d",ret);
	return 0;
}

在这里插入图片描述

(2)易错题:

分析下面的代码,推测最终的输出结果。

#include <stdio.h>
#include<string.h>
int main()
{
 const char*str1 = "abcdef";
 const char*str2 = "bbb";
 if(strlen(str2)-strlen(str1)>0)
 {
 printf("str2>str1\n");
 } 
 else
 {
 printf("srt1>str2\n");
 }
 return 0;
}

str1的长度肯定比str2要长,所以str2-str1肯定是负数,所以最终打印的应该是str1>str2。但是我们运行上面的代码,得到了下面的结果:
在这里插入图片描述
我们发现,代码的运行结果和我们的推测结果并不相符。其实原因就在于strlen函数的返回值。strlen函数的返回值是无符号整型。所以两个无符号整型做运算,最终得到的结果也一定是无符号整型,即大于等于零的数字。因此,最终打印的结果肯定是正的,于是就出现了刚才的打印结果。

3、strlen的实现

(1)循环

#include<stdio.h>
#include <assert.h>
size_t my_strlen(const char*str)
{
	assert(str);
	int count=0;
	while(*str!='\0')
	{
		count++;
		str++;
	}
	return count;
}

我在写上述代码的时候,我加了一行代码assert(str)。这是一个函数,这个函数的作用是保证指针的有效性。如果这个函数括号内的表达式为假,那么此时就会打印出一行错误信息,同时会阻止程序的进行,避免造成严重的后果。这里我们就是用assert函数来判断我们传入的指针是否为空指针。但是在应用这个函数的时候,我们要包括头文件:#Include<assert.h>

(2)函数递归

#include<stdio.h>
#include <assert.h>
size_t my_strlen(const char*str)
{
	assert(str);
	if(*str!='\0')
	{
		return 1+my_strlen(str+1);
	}
	else
	{
		return 0;
	}
}

二、strcpy函数

1、函数的用途:

在这里插入图片描述
我们先看函数的返回值类型,函数返回的是需要更改的那个字符串的首元素地址。
我们再看函数的形参列表,destination是需要被更改的那个字符串,由于这个字符串的内容会发生变化,所以不要用const 关键字去修饰。
而形参列表中的另外一个字符指针指向的是我们需要复制的内容的字符串地址。这个变量要用const修饰,避免发生修改。
然后,我们要了解一下这个函数体内部的实现原理:
在这里插入图片描述
我们发现,当我们利用strcpy函数,将arr2字符串中的内容复制给arr1的时候,出现了上述现象,即该函数仅仅将字符串arr2的第一个\0前的内容复制给了arr1,***包括\0。***由此我们就能够推断出,该函数是将arr2的首元素到第一个\0之间的元素复制给了arr1(包括\0)。

这个函数同样有一些点需要我们注意:

  • 源字符串必须以 ‘\0’ 结束。
  • 会将源字符串中的 ‘\0’ 拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变。
    如果源字符串中没有\0那么复制的内容将有一部分是随机值,因为该函数会不断地复制直到找到\0。如果arr1的空间不够大,那么arr2中的数组内容是放不下的,所以目标空间一定要很大。同时我们发现,arr1字符串调用函数前后,其字符串中的内容发生了变化,所以这个字符串必须是变量,而不能是常量字符串,同时我们在传入目标空间的指针的时候,也不能用const修饰,因为我们需要通过解引用的方式对其中 的内容进行修改。

2、函数的使用:

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = "XXXXXXXX";
	char arr2[] = "AAAA";
	strcpy(arr1,arr2);	
	return 0;
}

3、函数的实现:

char*my_strcpy(char*destination,const char*sourse)
{
	assert(destination);
	assert(sourse);
	char*ret=destination;
	while(*sourse!='\0')
	{
		*destination=*sourse;
		destination++;
		sourse++;
	}
	return ret;
}

上面的代码是我们最容易想到的,但是这个代码有一个问题,即我们无法将\0赋值给目标空间,所以我们对上述的代码进行修改,得到下面的代码:

char*my_strcpy(char*destination,const char*sourse)
{
	assert(destination);
	assert(sourse);
	char*ret=destination;
	while(*destination++=*sourse++)
	{
		;
	}

	return ret;
}

我相信上述代码中,最难理解的就是while循环这一部分,在while循环的小括号内,有一个赋值表达式,这个表达式的逻辑是,将字符赋值给*destination,当赋值表达式执行成功后,根据*destination的值进行条件判断。这样下来就形成了一种,先赋值后判断的循环逻辑。
同时,当赋值结束后,进行后置加加,赋值下一个字符。因此当遇到\0的时候先将其赋值给目标空间,然后此时在利用目标空间内的\0进行条件判断为假,终止循环。

三、strcat函数:

1、函数的用途:

在这里插入图片描述
这个字符串对我们而言相对陌生。这个函数的作用是在目标空间的结尾处,追加一段字符串,即将两个字符串拼接在一起。
有了对这个函数的初步认知后,我们先来分析一下这个函数的返回值类型,这个函数返回的是一个字符指针,这个指针指向的就是目标空间的地址。这个函数的形参列表和strcpy的形参列表相似。这里就不过多的介绍了。这个函数体的内部实现逻辑其实也很简单,就是在目标空间的末尾追加一段字符串而已,但是在这个过程中,有几个需要注意的点如下:

  • 目标空间中最初的末尾处的\0会被追加的字符串的第一个字符覆盖。
  • 源字符串必须以 ‘\0’ 结束。
  • 目标空间必须有足够的大,能容纳下源字符串的内容。
  • 目标空间必须可修改。

上述的注意事项其实是非常容易理解的,如果原始目标空间末尾处的\0没有被覆盖,那么最终依旧是打印到\0截至,也就是说后面的追加的内容不会被打印,因此从表面上看相当于没有追加。其次,目标空间必须足够大,以至于能够在原字符串的基础上容纳另外一个追加的字符串。

2、函数的使用:

#include<stdio.h>
#include<string.h>
int main()
{
	char str1[20]="aaaaa";
	char str2[]="xxx";
	printf("%s\n",strcat(str1,str2));
	return 0;
}

在这里插入图片描述

3、函数的实现:

char*my_strcat(char*destination,const char*sourse)
{
	assert(destination);
	assert(sourse);
	char*ret=destination;
	while(*destination!='\0')
	{
		destination++;
	}
	while(*destination++=*sourse++)
	{
		;
	}
	return ret;
}

首先我们为了保证指针的有效性,利用assert函数进行判断。接着我们利用while循环找到目标空间中\0的位置,接着我们在利用刚才的套路进行字符串的附加,最终得到结果。

四、strcmp函数

1、函数的用途:

在这里插入图片描述
这个函数其实非常常用了,就是比较两个字符串然后返回一个比较后的结果。但是我们需要注意的是这个函数的比较规则,这个函数会从两个字符串的第一位开始比较,比较的是二者的ASCII码值,如果二者不同,那么就直接返回对应的结果。如果相同,则继续比较第二位元素对应字符的ASCII码值,直至比较出差异。倘若整个字符串都和另外一个字符串相等,那么就会返回0,代表二者相同。
标准规定:

  • 第一个字符串大于第二个字符串,则返回大于0的数字。
  • 第一个字符串等于第二个字符串,则返回0。
  • 第一个字符串小于第二个字符串,则返回小于0的数字。

2、函数的使用:

#include<stdio.h>
#include<string.h>
int main()
{
	char str1[]="abcdef";
	char str2[]="abd";
	int ret=strcmp(str1,str2);
	if(ret>0)
	{
		printf("str1>str2\n");
	}
	else if(ret<0)
	{
		printf("str1<str2\n");
	}
	else
	{
		printf("str1=str2\n");
	}	
	return 0;
}

在这里插入图片描述

3、函数的实现:

方式1:

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

方式2:

int my_strcmp(const char*s1, const char*s2)
{
	assert(s1 && s2);
	while (*s1 == *s2)
	{
		if (*s1 == '\0')
		{
			return 0;
    	}
		s1++;
		s2++;
	}
	if (*s1 > *s2)
		return 1;
	else
		return -1;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值