C语言——常用字符串函数的总结与模拟实现

C语言本身是没有字符串类型的,字符串通常放在 常量字符串 中或者 字符数组 中。
字符串常量 适用于那些对它不做修改的字符串函数。

目录​​​​​​​

一、strlen函数

1、函数介绍:

2、模拟实现strlen

 (1)、计数器方法

(2)、指针减指针方法

(3)、递归方法

二、strcpy

1、函数介绍

 2、模拟实现strcpy

三、strcat

1、函数介绍

2、模拟实现strcat

四、strcmp

1、函数介绍

2、模拟实现strcmp

五、strncpy

1、函数介绍

2、模拟实现strncpy

六、strncat

1、函数介绍

 2、模拟实现strncat

七、strncmp

1、函数介绍

 2、模拟实现strncmp

八、strstr

1、函数介绍

 2、模拟实现strstr

中途随笔记:


一、strlen函数

1、函数介绍:

   用途:求字符串的长度
   格式: size_t strlen(const char* str);  ( size_t代表无符号整形:size_t==unsigned int )

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

 用简单的代码来展示下strlen的用途:

int len = strlen("abcdef");
printf("字符串“abcdef”的长度是:%d\n",len);
//输出结果为:
//            字符串“abcdef”的长度是:6

strlen函数是通过字符串末尾的 '\0' 来计算长度的,也就是说,只有碰到了'\0',strlen函数才会认为这个字符串结束了,如果没有'\0'结尾的话,strlen就会一直往回计算,直到碰到'\0'。

char arr[] = { 'a','b','c','d','e','f' };
int len1 = strlen(arr);
printf("arr 的长度是:%d\n",len1);
//输出结果为
//            arr的长度是:(随机值)
//如下图

 还有需要注意的一个地方,那就是strlen的返回类型是无符号整形,我们看以下代码:

if (strlen("abc") - strlen("abcdef") > 0)
	printf("strlen返回的是一个无符号整数\n");
else
	printf("strlen返回的是一个有符号整数\n");
//输出结果为:
//            strlen返回的是一个无符号整数

 因为strlen返回的都是无符号整数,所以两个无符号数进行运算所得的值肯定也是一个无符号数,那肯定就是一个大于0的整数,所以if里判断为真,输出上一句话。

但是这里要注意,使用strlen函数时一定要引入string.h头文件,不然的话,上面的代码就是输出下面那一句了:

 事实上,用所有函数都应该记得引入头文件,这是一个好习惯。

2、模拟实现strlen

 (1)、计数器方法

//模拟实现strlen 
//1、计数器
int my_strlen(const char* arr) {//计算长度不能改变源字符串内容,所以加个const稳妥
	int count = 0;
	while (*arr) {//判断指针指向的内容是否等于'\0'也就是是否等于0,等价于*arr!='\0'
		count++;
		arr++;
	}
	return count;
}

int main() {
	char arr[] = "abcdefghi";
	int len=my_strlen(arr);
	printf("arr 的长度是:%d\n", len);
	return 0;
}

(2)、指针减指针方法

//模拟实现strlen
//2.指针减指针
int my_strlen(const char* arr) {
	char* front = arr;//前指针
	char* rear = arr;//后指针
	while (*rear) {//直到后指针指向了'\0'
		rear++;
	}
	return rear - front;//此时两指针相减就是字符串的字符个数也就是长度
}
int main() {
	char arr[] = "abcdefghi";
	int len=my_strlen(arr);
	printf("arr 的长度是:%d\n", len);
	return 0;
}

(3)、递归方法

//模拟实现strlen
//3、递归
int my_strlen(const char* arr) {
	if (*arr == '\0')
		return 0;
	return 1 + my_strlen(arr + 1);
}
//主函数
int main() {
	char arr[] = "aasdvs  daci";
	int len=my_strlen(arr);
	printf("arr 的长度是:%d\n", len);
	return 0;
}

二、strcpy

1、函数介绍

用途:复制源字符串到一个新字符串里

格式:char* strcpy(char* destination,const char* source);

//destination是目标字符串,source是源头字符串

  •     源字符串必须以'\0'结束
  •     会将源字符串中的'\0'拷贝到目标空间
  •     目标空间必须足够大,以确保能存放源字符串
  •     目标空间必须可变

用简单的代码展示下strcpy的用途:

char arr1[] = "abcdef";
char arr2[] = "hello";
printf("原来的arr1==%s\n", arr1);
strcpy(arr1, arr2);
printf("新来的arr1==%s\n", arr1);
//输出结果为:
//            原来的arr1==abcdef
//            新来的arr1==hello

在使用strcpy函数时,也要注意'\0'的存在,源字符串必须以'\0'结尾,不然的话程序会出错:

 strcpy的 实现原理,它并没有把arr1全部变成了和arr2一模一样,它只是把前面与arr2长度相等的个数的字符串变成了arr2的字符串

 并且,目标字符串的空间必须大于源字符串,也就是说,arr1的空间必须比arr2大,不然怎么能容得下arr2呢:

还需要注意一点,目标的空间是要可变的,意思就是,目标字符串不能是一个常量字符串,不然程序也会崩溃:

 2、模拟实现strcpy

//1.模拟实现strcpy——简陋版
void my_strcpy(char* dest, char* src) {
	while (*src) {//等价于*src!='\0'
		*dest = *src;//将源字符串的每一个字符赋值给目标字符串
		dest++;
		src++;
	}
//当退出循环时,此时的src指向的是源字符串的'\0'并且并没有拷进目标字符串,
    *dest = *src;//所以在循环结束后将他考进
}

//2.模拟实现strcpy——精简版
void my_strcpy(char* dest, const char* src) {
	//这个循环条件的值就是*dest的值,当他拷贝完src里面的\0时,就会退出循环了
	while (*dest++ = *src++) {
		;
	}
}

//主函数
int main() {
	char arr1[] = "abcdefghikj";//目标字符串空间必须是可变空间,必须比源字符串空间大
	char arr2[] = "hello";
	strcpy(arr1, arr2);
	printf("%s", arr1);//hello
	return 0;
}

三、strcat

1、函数介绍

用途:给目标字符串的后面追加一个字符串

语法:char* strcat(char* destination,const char* source);

//destination 是目标字符串,source 是源头字符串

  • 源字符串必须以'\0'结束
  • 目标空间必须足够大,能容下源字符串的内容
  • 目标空间必须可更改
  • 不能自己给自己追加

 用简单的代码展示下strcat的用途:

char arr1[30] = "hello";
char arr2[] = "world";
printf("原来的arr1==%s\n", arr1);
strcat(arr1, arr2);
printf("新来的arr1==%s\n", arr1);
//输出结果为:
//            原来的arr1==hello
//            新来的arr1==helloworld

(1)、在使用strcat时,也要注意源字符串的'\0':

 (2)、目标空间必须足够大:

 (3)、并且目标空间是可以更改的,不然程序会崩溃

 (4)、假如一个字符串自己给自己追加是什么样子?依旧会崩溃:

2、模拟实现strcat

//模拟实现strcat
char* my_strcat(char* dest, const char* src) {
//最后需要返回dest,所以先用个ret先把dest存起来
	char* ret = dest;
	//1、找到目标字符串的'\0'
	while (*dest != '\0') {
		dest++;
	}
	//2、追加
	while (*dest++ = *src++) {
		;
	}
	return ret;
}
//主函数
int main() {
	char arr1[30] = "hello";
	char arr2[] = "world";
	strcat(arr1, arr1);
	printf("%s\n",arr1);
	return 0;
}

四、strcmp

1、函数介绍

用途:比较两个字符串的大小

语法:int strcmp(const char* str1,const char* str2);

  •     标准规定:
  •         第一个字符串大于第二个字符串,则返回大于0的数字
  •         第一个字符串等于第二个字符串,则返回等于0的数字
  •         第一个字符串小于第二个字符串,则返回小于0的数字

  用简单的代码展示下strcmp的用途:

char* p1 = "abc";
char* p2 = "abd";
int ret = strcmp(p1, p2);
if (ret > 0)
	printf("p1>p2\n");
if (ret == 0)
	printf("p1==p2\n");
if (ret < 0)
	printf("p1<p2\n");
//输出结果:
//            p1<p2

(1)、strcmp比较的不是字符串的长度大小,而是字符的ASCII码值, 以第一个不相等的字符为准,如:

前面两个字符串的字符a和字符b都相等,所以继续往后比较,然后因为字符'c'的ASCII码值比字符'h'的ASCII码值小,所以会返回一个负数,代表着p1<p2。 

(2)、当p1与p2不相等时,strcmp返回的值不一定就是-1和1。

strcmp的返回值除了0就是一个大于0或者小于0的数,但不一定是1和-1,上面的程序输出的是-1那是因为编译器的问题,实际上不是-1,应该是两个ASCII码值的差。

2、模拟实现strcmp

//模拟实现strcmp
//my_strcmp函数
int my_strcmp(const char* str1, const char* str2) {
	//比较,两串一直相等到'\0'时就返回0,说明他俩相等
    //不等就直接退出循环
	while (*str1 == *str2) {
		if (*str1 == '\0')
			return 0;
		str1++;
		str2++;
	}
	//退出循环了说明只能大于和小于了
	if (*str1 > *str2)
		return 1;//大于
	else
		return -1;//小于
}
//主函数
int main() {
	char* p1 = "abc";
	char* p2 = "abd";
	int ret = my_strcmp(p1, p2);
	printf("这个返回值是:%d\n", ret);
	if (ret > 0)
		printf("他代表着:p1>p2\n");
	if (ret == 0)
		printf("他代表着:p1==p2\n");
	if (ret < 0)
		printf("他代表着:p1<p2\n");
	return 0;
}

上面这个程序是用1和-1来表示大于小于0,也可以直接输出实际的大于小于0的数:

return *str1-*str2;
//把上面my_strcmp程序的if else语句直接换成这句


五、strncpy

1、函数介绍

用途:拷贝指定长度的字符

语法:char* strncpy(char* destination,const char* source,size_t num);

//destination 是目标字符串,source 是源头字符串,num是指定长度

  •     拷贝num个 字符从源字符串到目标空间
  •     如果源字符串的长度小于num,则拷贝完字符串后,在目标的后面追加0,直到num个
  •     以及strcpy的各种注意

   用简单的代码展示下strncpy的用途:

char arr1[10] = "abcdefe";
char arr2[] = "hello";
printf("原来的arr1:%s\n", arr1);
strncpy(arr1, arr2, 4);
printf("新来的arr1:%s\n", arr1);
//输出结果:
//            原来的arr1:abcdefe
//            新来的arr1:hellefe

然后我们看一下内存中 arr1的变化:

 能看得出来,strncpy也是把目标字符串里的指定的长度的字符变成源字符串里的字符,与strcpy不同的是:strcpy是把整个目标字符串拷贝给了目标字符串,包括'\0',但是strncpy不是,它只把指定长度的字符拷给目标字符串

那如果这个指定的长度大于了源字符串的长度怎么办?会自动补上'\0',差多少补多少。

 从图看出,arr2的长度只有5,加上一个'\0'也只有6,而我们要拷贝8个字符去arr1里,显然还差两个字符,到最后输出了arr2的字符串,那我们再看内存:

 看得出,在arr2的基础上,又多了两个'\0',这就是strncpy自己补上去的两个'\0'。

2、模拟实现strncpy

//模拟实现strncpy
//my_strncpy函数
char* my_strncpy(char* str1, const char* str2, int num) {
    //先用一个 指针把原来的指针存起来,不然函数过后字符串就会被改变了
	char* dest = str1;
	//num和str2的值都不能为0,任一为0退出循环
	while (num && *str2) {
		*str1 = *str2;
		num--;
		str1++;
		str2++;
	}
	//上面循环退出时,并没有把'\0'拷贝走
	if (num != 0) {
		while (num--) {
			*str1 = '\0';
			str1++;
		}
	}
	return dest;
}
//主函数
int main() {
	char arr1[20] = "abcdefeghi";
	char arr2[] = "hello";
	printf("原来的arr1:%s\n", arr1);
	strncpy(arr1, arr2, 8);//arr2位数不够会补'\0'
	printf("新来的arr1:%s\n", arr1);
	return 0;
}

 上面的my_strncpy函数是比较基础的,还可以优化优化:

//模拟实现strncpy
//优化my_strncpy函数
char* my_strncpy(char* str1, const char* str2, int num) {
	char* dest = str1;

	while (num && (*str1++=*str2++)) {
		num--;
	}
	//上面循环退出时,已经拷走了'\0'
	if (num) {
		while (--num) {
			*str1++ = '\0';
		}
	}
	return dest;
}

这两个函数的区别就在于num--和--num。


六、strncat

1、函数介绍

用途:追加指定长度的字符串

语法:char* strncat(char* destination,const char* source,size_t num);

  •     在目标字符串的'\0'后追加,会覆盖当前'\0',并追加完后给上 一个'\0'
  •     追加的长度比源字符串长的话,只追加一个源字符串
  •     以及strcat的相关注意

    用简单的代码展示下strncat的用途:

char arr1[10] = "hello";
char arr2[] = "world";
printf("原来的字符串:%s\n", arr1);
strncat(arr1, arr2, 4);
printf("新来的字符串:%s\n", arr1);
//输出结果:
//            原来的字符串:hello
//            新来的字符串:helloworl

我们先看看内存里的情况:

 和strcat一样,都是从目标字符串的'\0'开始追加,并且覆盖这个'\0',我们先看看strncat是不是根据'\0'来进行追加的:

 显然,两图比较可以看出,strncat确实是根据'\0'来进行追加的,这和strcat是一样的,并且strncat只追加给定长度的字符串

要是给定的要追加的长度大于我源字符串的长度怎么办?是像strncpy那样自动补加'\0'吗?并不是,如果长度大于源字符串长度,那么只会追加一个字符串,并不会另加其他:

 显然,指定长度为8时,并没有在拷贝后的字符串里出现多余的'\0',说明strncat不会像strncpy那样自动填补差的'\0'

 2、模拟实现strncat

//模拟实现strncat
//my_strncat函数
char* my_strncat(char* str1, const char* str2, int num) {
	char* dest = str1;
	//先找到目标字符串的'\0'
	while (*str1++) {
		;
	}
	//因为上面多加了一次所以需要减回去
	str1--;
	//然后从'\0'的地方拷贝
	while (num--&&(*str1++ = *str2++)) {
		;
	}
	return dest;
}
//主函数
int main() {
	char arr1[20] = "hello\0xxxxxxxx";
	char arr2[] = "world";
	printf("原来的字符串:%s\n", arr1);
	strncat(arr1, arr2, 8);
	printf("新来的字符串:%s\n", arr1);
}

七、strncmp

1、函数介绍

用途:比较指定长度的字符串

语法:int strncmp(const char* str1,cosnt char* str2,size_t num);

  •     比较前num个字符
  •     比较到出现一个字符不一样或者一个字符串结束或者num各字符全部比较完
  •     以及strcmp相关注意

 用简单的代码展示下strncmp的用途:

const char* p1 = "abcdefg";
const char* p2 = "abcf";
int ret1 = strncmp(p1, p2, 4);
printf("%d\n", ret1);
//输出结果:
//            -1
//因为第四个字符‘d’小于‘f’

 int strncmp(const char* str1,cosnt char* str2,size_t num);

这里的num表示的是要比较的字符的个数,比如num是3的话,那么就只比较p1、p2的前三个字符,也就是abc,那么两个字符串的前三个字符都相等,所以返回值是0:

 如果num的值大于两个字符串的长度或者大于任意一个字符串的长度的话,那么只会比较到:

  • 当第出现第一个字符不一样的时候
  • 当一个字符串的字符比较完后

 strncmp的返回值规则与strcmp的规则是一样的:

 2、模拟实现strncmp

//模拟实现strncmp
//my_strncmp函数
int my_strncmp(const char* p1, const char* p2,int num) {
	//以num作循环次数条件
	//一有不同的字符就直接返回
	for(int i=0;i<num;i++){
		//挨个比较字符的大小
		if (*p1 > *p2) 
			return 1;
		if (*p1 < *p2)
			return -1;
		//指针移到下一个字符
		p1++;
		p2++;
	}
	//退出循环说明两个字符串相同
	return 0;
}
//主函数
int main() {
	const char* p1 = "abcdefg";
	const char* p2 = "abcf";
	int ret1 = strncmp(p1, p2, 4);
	int ret2 = my_strncmp(p1, p2, 4);
	printf("%d  %d\n", ret1,ret2);
	return 0;
}

八、strstr

1、函数介绍

用途:找到子串在一个字符串中第一次出现的位置。

语法:char* strstr(const char* p1,const char* p2);

  • 返回的是子串在主串中的第一个字符的位置

  用简单的代码展示下strstr的用途:

char* p1 = "abbcdef";
char* p2 = "bbc";
char* ret1 = strstr(p1, p2);
if (ret1 == NULL)
	printf("子串不存在\n");
else
	printf("%s\n", ret1);	
//输出结果:
//            bbcdef
//返回的是子串bbc的第一个字符的位置,也就是b的位置

如果p2是p1的子串,就会返回p1的位置。

 上图可以看出位置与字符串的关系,是该字符的位置也是以该字符为开头的字符串的位置,所以打印返回的值时是打印的剩余的字符串:

如果不是子串,程序就会返回一个NULL。

 2、模拟实现strstr

#include<string.h>
//模拟实现strstr
//第一种方法:
char* my_strstr(const char* p1, const char* p2) {
	//如果子串是空串直接返回主串
	if (*p2 == '\0')
		return p1;
	//新建两个指针
	char* s1 = p1;//主串指针,s1作移动指针,p1函数返回值的指针
	char* s2 = p2;//子串指针,s2作移动指针,p2一直指向子串的第一个字符不变
	//当主串指针没有到达最后'\0'处
	while (*s1) {
		//如果两指针指向的字符相同,指针后移继续比较
		if (*s1 == *s2) {
			s1++;
			s2++;
		}
		//如果不同,p1指向当前s1后移一个的位置处,s2变为原位
		else {
			p1 = ++s1; 
			s2 = p2;
		}
		//当s2直到了'\0'并且两个字符串的两个指针之差相等时,说明p2是p1的子串
		if (*s2=='\0'&&(s1 - p1) == (s2 - p2))
			return p1;
	}
	//退出循环说明p2不是p1的子串,返回null
	return NULL;
}
//第二种方法:
char* my_strstr(const char* p1, const char* p2) {
	//如果子串是空串直接返回主串
	if (*p2 == '\0')
		return p1;
	//创建三个指针
	char* s1 = p1;	//代替主串
	char* s2 = p2;	//代替子串
	char* cur = p1;	//辅助指针,用来表示可能匹配成功的位置
	//当主串指针没有指到'\0'时
	while (*s1) {
		//如果后面有匹配失败,那么s1就回到第一次匹配成功的地方
		s1 = cur;
		//如果后面匹配失败,s2回到p2开头的地方,重新匹配
		s2 = p2;
		//在两个字符串都没有比较完时并且两个字符相等
		while (*s1!='\0' && *s2 != '\0' && (*s1 == *s2)) {
			s1++;
			s2++;
		}
		//如果子串比较到了最后的'\0'处,那说明前面的字符都匹配成功了
		if (*s2=='\0')
			return cur;//返回第一个字符的位置
		//到这说明没有匹配成功,所以将cur向后移动一个
		cur++;
	}
	//退出循环说明p2不是p1的子串,返回null
	return NULL;
}
//主函数:
int main() {
	char* p1 = "abbcdef";
	char* p2 = "bbc";
	char* ret1 = strstr(p1, p2);
	char* ret2 = my_strstr(p1, p2);
    //可以用来比较自己写的strstr与库函数的结果一不一样
	if (ret1 == NULL)
		printf("子串不存在\n");
	else
		printf("%s\n", ret1);	
	if (ret2 == NULL)
		printf("子串不存在\n");
	else
		printf("%s\n", ret2);
	return 0;
}

中途随笔记:

1、用字符数组存一个字符串时,用键盘输入字符串,会自动将每一个字符分配到数组每一个空间,而整形数组不行。(getchar和scanf结果都一样)

2、NULL 代表的是 空指针,Null或NUL 代表的是 '\0';

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

多低调

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值