字符串函数

strlen的使用和模拟实现

strlen用来计算字符串的长度,从给定起始位置到'\0'。

函数的返回值是size_t,是无符号的。

传递给strlen函数的形参是字符串的首元素地址。

想使用字符串函数,需要增加头文件#include<string.h>

 对于库函数strlen,使用方法如下:

 我们可以看到,strlen计算得到arr字符型数组的长度为6,可知strlen计算的结果不包括'\0'。

 模拟实现strlen函数,我们有多种方法:

方法1,通过指针变量字符数组,指针每遍历一个字符,计数器自加,最后返回计数器的值就是字符数组的长度。

int my_strlen(const char*arr) {
	int num = 0;
	assert(arr);
	while (*arr) {
		num++;
		arr++;
	}
	return num;
}
int main() {
	char arr[] = { "hellow" };
	int len=my_strlen(arr);
	printf("%d\n", len);
}

方法2,在函数时,我们学习了递归的思想,方法1的想法其实可以转化成递归的方式:

在初始的返回值我们可以设置成1,在返回值通过递归可以获得字符串的长度。

但是在这里我们需要注意一个问题,如果字符串的长度本身就为0,那么我们就不能返回1,因此我们可以通过assert断言来判断传递给函数的指针是否为空指针,如果是空指针就返回0,非空指针就通过递归获取字符串的长度。

int my_strlen2(char* arr) {
	assert(arr);
	if (*arr == '\0') {
		return 0;
	}
	else {
		return 1 + my_strlen2(arr+1);
	}
}

注:使用assert断言需要增加头文件#include<assert>。 

 方法3:因为指针-指针返回的是一个整数,所以我们可以通过两个指针来计算字符串的长度

我们可以设置一个指针指向字符串的首元素,然后让指针形参进行遍历,当遍历到'\0'时跳出循环,此时指针指向'\0'前一个字符。最后返回指针与形参相减的结果,这个结果就是字符串的长度。

int my_strlen3(char*arr) {
	char* p = arr;
	while (*p!='\0') {
		p++;
	}
	return p - arr;
}
int main() {
	char arr[] = { "hellow" };
	int len = my_strlen3(arr);
	printf("%d", len);
}

 

strcpy的使用和模拟实现

strcpy函数的作用是将源字符串中的数据复制到目标字符串。 

在cpluscplus中我们可以了解到,strcpy函数的形参应该是目标空间的起始地址和源字符串的起始地址。 

 

目标字符串中的大小应该足够长。

strcpy函数返回的是目标空间的起始地址。

源字符串中的'\0',也会被拷贝到目标空间中。 

对于strcpy的使用:

库函数strcpy的使用
int main() {
	char arr1[] = { "hellow" };
	char arr2[10] = { 0 };//目标字符串中的大小应该足够长。
	strcpy(arr2, arr1);
	for (int i = 0; i < strlen(arr2); i++) {
		printf("%c", arr2[i]);
	}
}

 

我们看到,arr1已经成功被拷贝到arr2中,下面让我们通过自己的代码模拟实现strcpy。

在模拟实现中,我们需要用一个指针记录目标空间的初始地址,因为一旦开始交换空间时,目标空间的指针就会发生改变,当我们拷贝完后返回目标空间的指针时就无法正确找到目标空间,因此记录目标空间的初始地址很有必要。

char* my_strcpy(char* arr2, const char* arr1) {//被复制的一方不需要改变,用const修饰
	char* ret = arr2;//先记录下拷贝数组的首地址,因为数组是连续存放的,知道数组首元素地址等于知道数组地址
	while ((*arr2++ = *arr1++)) {
		//当 *arr1 是 '\0'(即字符串的终结符)时,赋值操作会将 '\0' 赋值给 *arr2,
		//并且 '\0' 的值是 0。while 循环的条件是 0(即假),因此循环会退出。
		;
	}
	return ret;
}
int main() {
	char arr1[] = { "hellow" };
	char arr2[10] = { 0 };
	my_strcpy(arr2, arr1);
	for (int i = 0; i < strlen(arr2); i++) {
		printf("%c", arr2[i]);
	}
}

 

 

strcat的使用和模拟实现

strcat函数可以将一个字符串拼接到另一个字符串上,但是两个字符串的空间不能有重叠。

下面是库函数中的使用例子:我们可以通过strcat自己给自己追加。 

#include<stdio.h>
#include<string.h>
//库函数使用
int main() {
	char arr1[20] = { "hellow" };//追加的数组要确定长度,不然会错误
	/*char arr2[] = { "nihao" };*/
	strcat(arr1, arr1);
	printf("%s", arr1);
	
}

 也可以插入别的字符串:

int main() {
	char arr1[20] = { "hellow" };//追加的数组要确定长度,不然会错误
	//char arr2[] = { "nihao" };
	strcat(arr1, "abc");
	printf("%s", arr1);
	
}

 我们需要注意:

源字符串必须以'\0',结束。

函数需要确定从哪个位置开始追加,所以目标字符串中也必须要有'\0'。

目标空间必须可以修改。

目标空间必须要大,否则无法容纳下源字符串。

 而对于模拟实现,其思想与strcpy基本一致:

目标空间指针遍历自身找到'\0'的位置,源字符串拷贝从目标空间的'\0'位置开始拷贝直到完成。

相较于strcpy的模拟实现,多了一步先让目标空间的遍历。

char* my_strcat(char* arr1, const char* arr2) {
	char* ret = arr1;
	while (*arr1) {
		arr1++;
	}
	while ((*arr1++) = (*arr2++)) {
		;
	}
	return ret;

}
int main() {
	char arr1[20] = { "hellow" };
	char arr2[] = { "nihao" };
	my_strcat(arr1, arr2);
	printf("%s", arr1);

}

对于模拟实现的结果也与库函数中的strcat一致。 

strcmp的使用和模拟实现

strcmp函数的作用是比较两个字符串的大小,比较两个字符串的大小,其实是比较对应位置上的ASCII码值的大小。

规定:第一个字符串>第二个字符串,返回大于0的数字;

           第一个字符串=第二个字符串,返回等于0的数字;

           第一个字符串<第二个字符串,返回小于0的数字;

 库函数中strcmp的使用:.

int main() {
	char arr1[] = { "abc" };
	char arr2[] = { "bcd" };
	int num=strcmp(arr1, arr2);
	printf("%d", num);

}

由于首个字符不一致,所以比较ASCII码,返回的结果是<0的数,可知arr1<arr2 。

对于模拟实现的方法:

通过指针找到两者对应位置不一样的指针指向,再通过当前指针的解引用相减来比较大小。函数的返回类型是int类型。

int my_strcmp(const char* arr1, const char* arr2) {
	//先通过循环找到不相等的位置,如果此时
	while ((*arr1 == *arr2)) {
          //如果遍历到末尾两者都相等,证明两个字符串相等,此时返回0
		if (*arr1 == '\0' && *arr2 == '\0') {
			return 0;
		}
		arr1++;
		arr2++;
	}
       //通过解引用获取当前指针指向的字符,字符的相减在计算机中自动转换成ASCII码进行计算
	return *arr1 - *arr2;
}
int main() {
	char arr1[] = { "abc" };
	char arr2[] = { "abc" };
	int len=my_strcmp(arr1, arr2);
	printf("%d", len);
}

得到的结果与库函数一致,模拟实现成功。 

 

strstr的使用和模拟实现

strstr函数的作用是在主串中寻找子串,,并返回子串第一次出现的位置

传递给函数的两个形参是主串的地址与待寻自串的地址。

strstr库函数的使用:

int main() {
	char arr1[]{ "abcdefgh" };
	char*p=strstr(arr1, "def");
	printf("%s", p);
	
}

 返回的是子串第一次出现的位置:

而对于strstr的模拟实现就稍微有点复杂了。

假设下面两串字符串,第二行字符串是要寻找的子串。

对两串字符串都进行遍历,当第一行与第二行同时指向第一个c,此时可能是子串出现的位置;另外再用一个指针指向不确定是否为子串位置的,我们将这个指针称为指针p。

随后继续向后遍历,发现第一行字符串的位置还不是子串出现的位置,这时我们需要将第二行指针进行重置,将第二行指针回到初始位置!

                               

此时待确定位置的指针p向后走一位(因为此处不是子串开始出现的位置,所以向后移动),而对于第一行的指针,则需要返回指针p的位置,然后重置后的第一行指针与第二行指针继续向后遍历。

至此,函数的主逻辑基本实现。但是有一种特殊情况,如果子串本来就是空字符串,那么我们就应该返回主串的初始位置。

const char* my_strstr(const char* str1, const char* str2) {
	const char* p1 = str1;//记录str1的初始位置
	const char* p2 = str2;//记录str2的初始位置
	const char* ret = str1;///记录可能开始相同的位置
	//特殊情况,查找的子串是空子串
	if (*p2 == '\0') {
		return p1;
	}
	while (p1) {
		//第一次循环
		p1 = ret;//第一次开始是首元素的位置,后面不断更新
		p2 = str2;//后续如果查找到一半发现不是子串,通过循环可以直接重置指针
		while (p1 && p2 && *p1 == *p2) {
			p1++;
			p2++;
		}
         //如果p2刚好是'\0',则子串已经找到了,返回记录可能相同位置的指针ret
		if (*p2 == '\0') {
			return ret;
		}
		ret++;
	}
	return NULL;
}
int main() {
	char arr1[] = { "abbbcdef" };
	char arr2[] = { "bbc" };
	const char*len=my_strstr(arr1,arr2);
	printf("%s", len);
	
}

输出结果与库函数一致,模拟实现成功。 

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值