进阶知识:各字符串函数的使用和模拟实现


一、strlen的使用和模拟实现

1函数基本信息

size_t strlen(const char* str);

做用是:返回字符串中’\0’之前的字符个数,不包括’\0’


2易错情况

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "bbc";

	if (strlen(arr2) - strlen(arr1) > 0)//解决方法:(int)strlen(arr2) - (int)strlen(arr1)
	{  //3             //6
		printf("arr2>arr1");//这里只能是这个结果,因为两个size_t类型的数据相减,得到的也是size_t类型的数据,所以结果永远>0
	}
	else
	{
		printf("arr2<=arr1");
	}
	return 0;
}

具体原因:
结果得到-3
原码:10000000 00000000 00000000 00000011
反码:11111111 11111111 11111111 11111100
补码:11111111 11111111 11111111 11111101 - 内存中存的是这个
因为是size_t类型数据,所以就没有符号位的概念了,所以结果是一个正数,而正数的原码=反码=补码
所以11111111 11111111 11111111 11111101会得到得到一个非常大的正数


3模拟实现 - 递归法

size_t my_strlen( const char* str)
{
	if (*str == '\0')
	{
		return 0;
	}
	else
	{
		return 1 + my_strlen(str + 1);
	}
}

int main()
{
	char arr[] = "abcdef";
	size_t len = my_strlen(arr);
	printf("%zd\n", len);

	return 0;
}

二、strcpy的使用和模拟实现

1.函数基本信息

char* strcpy (char* 目标位置,const char* 源头位置)
作用:将源头数据拷贝到目标位置

注:源字符串里面必须有’\0’
注:'\0’也会被拷贝
注:目标空间必须足够大,且目标空间必须可修改


2.易错情况

int main()
{
	char arr1[] = "abcdef";
	char arr2[20] ="xxxxxxxxxxxxxx";
	char* p = "xxxxxxxxxxx";

	//arr2 = arr1;//是错误的,因为数组名是地址,是常量值

	strcpy(arr2, arr1);

	strcpy(p, arr1);//这里在拷贝的时候会出现报错,因为p指向的是常量字符串,而常量字符串不可修改

	printf("%s\n", arr2);
	printf("%s\n", p);

	return 0;
}

3.strcpy的模拟实现

char* my_strcpy(char* dest, const char* src)
{
	assert(dest != NULL);
	assert(src != NULL);

	char* ret = dest;

	while (*dest++ = *src++)//不仅完成了拷贝,而且完成拷贝'\0'之后停止
	{
		
	};
	return ret;//返回的是目标空间的起始地址
}

int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = "xxxxxxxxx";

	//char* p = my_strcpy(arr2,arr1);
	//printf("%s\n", p);

	printf("%s\n", my_strcpy(arr2, arr1));//返回值的作用 - 链式访问效果

	return 0;
}

三、strcat的使用和模拟实现

1.函数基本信息

strcat字符追加 - 在目标数据后面加源头数据

char* strcat(char * 目标地址,const char 源头地址)
返回的是目标空间的起始地址

注:
1.目标字符串里面必须有’\0’,且目标数组有足够的空间去追加,且目标空间必须可修改
2.源字符串必须有’\0’

2.strcat的模拟实现

char* my_strcat(char* dest, const char* src)
{
	assert(dest && src);//NULL - 0

	char* ret = dest;
	//1.找到dest指向的字符串中的'\0'
	while (*dest)//当dest指向'\0'时停止
	{
		dest++;
	}
	//2.数据的拷贝
	while (*dest++ = *src++)
	{
		;//空语句 - while循环要做的事情做完了,但又得有下面的语句,这时候可以用空语句 - 即就写一个;
	}
	return ret;//返回的是目标空间的起始地址
}

int main()
{

	char arr1[20] = "hello";//想在后面加上world
	char arr2[20] = "world";

	//my_strcat(arr1, arr2);
	//printf("%s\n", arr1);

	printf("%s\n", my_strcat(arr1, arr2););

	return 0;
}

3.问题思考

是否可以在arr1后面追加arr1?

int main()
{
	char arr1[20] = "abc";

	printf("%s\n", my_strcat(arr1, arr1));

	return 0;
}

答:my_strcat不行
原因:当dest指向arr1中的‘\0’时,src指向arr1中的’a’
所以会把arr1中的’\0’换成’a’ - 此时arr1 = abca’\0’‘\0’…
然后接着往后换 - 此时arr1 = abcab’\0’…
… - =abcabc…
… - =abcabca…
src要一直找到’\0’才停止,但是dest在src后面,所以会把后面的’\0’都换掉
导致src找不到’\0’而一直往下换下去
最终死循环把程序弄崩溃

总结:当目标的’\0’被换掉时,源头的’\0’也被换掉了
<string.h>库里面的strcat可以做到strcat(arr1,arr1),因为经过了特殊处理

四、strcmp的使用和模拟实现

1.函数基本信息

作用:字符串比较 - 两个字符串对应位置上的字符进行比较 - 比较的是ASCII码值

int strcmp(const char* str1,const char* str2);

str1>str2 返回>0的数
str1=str2 返回=0的数
str1<str2 返回<0的数

int main()
{
	//int r = strcmp("abcdef", "abq");//r=-1
	//int r = strcmp("abq", "abcdef");//r=1
	//int r = strcmp("abcdef", "abcdef");//r=0
	//printf("%d\n", r);

	if ("abq" > "abcdef")//这个表示用两个字符串的地址比较 - 即首元素a的地址
	{ //a的地址  //a的地址

	}
	return 0;
}

2.strcmp模拟实现

法一

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)//如果两个字符串完全相等,那么最后都会变成'\0'
	{
		if (*str1 == '\0')
		{
			return 0;
		}
		str1++;
		str2++;
	}
	if (*str1 > *str2)
	{
		return 1;
	}
	else
	{
		return -1;
	}
}

法二

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)//如果两个字符串完全相等,那么最后都会变成'\0'
	{
		if (*str1 == '\0')
		{
			return 0;
		}
		str1++;
		str2++;
	}
	return *str1 - *str2;
}

int main()
{
	if (my_strcmp("abcdef", "abq") > 0)
	{
		printf("abcdef > abq");
	}
	else if (my_strcmp("abcdef", "abq") < 0)
	{
		printf("abcdef < abq");
	}
	else
	{
		printf("abcdef = abq");
	}

	return 0;
}

五、strncpy函数的使用和模拟实现

1.函数基本信息

char* strncpy(char* 目标, const char* 源头, size_t 数量);

2个参数:
strcpy,strcat,strcmp
长度不受限制的字符串函数
3个参数:
strncpy,strncat,strncmp
长度受限制的字符串函数
相对安全

int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = "xxxxxxxxxx";//只拷贝abc到arr2

	//strncpy(arr2, arr1, 3);
	strncpy(arr2, arr1, 8);//当arr1没有这么多的时候,会拿'\0'来补,直到补足数量

	printf("%s\n", arr2);

	return 0;
}

2.strncpy模拟实现

char* my_strcpy(char* str1, const char* str2,int num)
{
	int i = 0;
	while (1)
	{
		if (*(str2 + i) == '\0')

		{
			*(str1 + i) = *(str2 + i);

			return str1;
		}
		else
		{
			*(str1 + i) = *(str2 + i);
		}
		i++;
		if (i == 5)
		{
			return str1;
		}
	}
}
int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = "xxxxxxxxx";
	printf("%s\n", my_strcpy(arr2, arr1,5));
	return 0;
}

六、strncat函数的使用和模拟实现

1.函数基本信息

char* strncat(char* 目标,const char* 源头,size_t 数量)
即指定追加多少个字符

int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = "qqqq";

	strncat(arr2, arr1, 4);//即追加abcd - 虽然追加4个,但是会在后面补一个'\0',相当于追加了5个

	printf("%s\n", arr2);


	return 0;
}

2.strncat模拟实现

char* my_strncat(char* str1, const char* str2,int num)
{
	int i = 0;
	while (*(str1 + i) != '\0')
	{
		i++;
	}
	char* ps = str1 + i;
	int m = 0;
	for (m = 0;; m++)
	{
		if (m == num)
		{
			*(str1 + i + m) = '\0';
				return str1;
		}
		if (*(str2 + m) == '\0')
		{
			*(str1 + i + m) = *(str2 + m);
			return str1;
		}
		else
		{
			*(str1 + i + m) = *(str2 + m);
		}
	}
}
int main()
{
	char arr1[20] = "hello";
	char arr2[20] = "world";
	printf("%s\n", my_strncat(arr1, arr2,3));
	return 0;
}

七、strncmp函数的使用

1.函数基本信息

int strncmp(const char* str1,const char* str2,size_t 数量)
即比较几个字符

int main()
{
	char arr1[] = "abcd";
	char arr2[20] = "abq";

	//int r = strncmp(arr1, arr2, 3);//-1
	int r = strncmp(arr1, arr2, 2);//0

	printf("%d\n", r);

	return 0;
}

八、strstr的使用和模拟实现

1.函数基本信息

strstr作用:在一个字符串中,找另外一个字符串 - 即找一个子字符串是否存在

const char* strstr(const char* str1,const char* str2);

即在str1中str2,找到了就返回子字符串的地址(子字符串的起始地址);没找到就返回NULL(空指针)

//注:返回的是第一次出现的地址

int main()
{
	char arr1[] = "abcdefabcdef";
	char arr2[] = "cde";
	//char arr2[] = "cdq";

	const char* ret = strstr(arr1, arr2);

	if (ret == NULL)
	{
		printf("没找到");
	}
	else
	{
		printf("%s\n", ret);
	}


	return 0;
}

2.模拟实现strstr

常规写法 - 暴力查找

const char* my_strstr(const char* str1, const char* str2)
{
	const char* s1 = NULL;
	const char* s2 = NULL;
	const char* cur = str1;

	//特殊情况的处理1
	assert(str1 && str2);
	//特殊情况的处理2
	if (*str2 == '\0')
	{
		return str1;
	}

	while (*cur)//cur指向'\0'时停止
	{
		s1 = cur;
		s2 = str2;
		while(*s1 != '\0' && *s2!= '\0' && * s1 == *s2)
		{
			
			s1++;
			s2++;
		}
		if (*s2 == '\0')//当*s2=='\0'的时候,说明已经完全找到str2了
		{
			return cur;
		}
		cur++;
	}
	//出了while,说明把str1找遍了,也没有找到str2
	return NULL;
}


int main()
{
	char arr1[] = "abcdefabcdef";
	char arr2[] = "cde";
	//char arr2[] = "cdq";

	const char* ret = my_strstr(arr1, arr2);

	if (ret == NULL)
	{
		printf("找不到");
	}
	else
	{
		printf("%s\n", ret);
	}

	return 0;
}

关于字符串查找有一种算法:KMP算法
特点1:高效
特点2:代码比较难,难写

九、strtok函数的使用

1.函数基本信息

作用:可以把字符串里面由分割符隔开的一段段提取出来

例如:想从"abcdef@hijklmn.opq"里面提取出abcdef,hijklmn,opq这三个部分 - 注:这里面的@和.为分割符

char* strtok(char* str,const char* sep)

2.使用方法

1.sep指向一个字符串,定义了用作分割符的字符集合 - 如上面例子中的sep指向"@."

2.str指向一个字符串,这个字符串包含0个或多个sep字符串中包含的分割符,被割下来的部分称为标记 - 即有几段就有几个标记

注1:当str指向NULL时,有0个标记;当字符串中没有sep中的分割符时,有1个标记
当strtok找到一个标记的时候,会在后面加’\0’,并返回指向这个标记的指针
如"abcdef@hijklmn.opq"找到第一个标记"zbcedf"时,会把’@‘变成’\0’即"abcdef\0hijklmn.opq",然后返回’a’的地址
找到第二个标记"hijklmn"时会把’.‘变成’\0’即"abcdef\0hijklmn\0opq"

注2:strtok会改变str字符串,所以str字符串一般是临时拷贝的字符串且可修改

3.当str指向的第一个参数不是NULL时,会找到str中的第一个标记,且strtok会保存被改为’\0’的’@'的位置,并返回’a’的地址

4.当str指向的第一个参数为NULL时,从这个被保存的位置往后找下一个标记,且strtok会保存被改为’\0’的’.'的位置,并返回’h’的地址

5.如果字符串不存在更多的标记,即找完了,返回NULL

int main()
{
	char arr[] = "abcdef@hijklmn.opq";
	char tmp[30] = { 0 };
	const char* sep = "@.";

	strcpy(tmp, arr);//tmp成了arr的临时拷贝
	//切割tmp的数据就可以了

	//演示 - 这个办法没问题,但是不实用
	//char* p = strtok(tmp, sep);
	//printf("%s\n", p);//第一个标记"abcdef"
	//
	//p = strtok(NULL, sep);
	//printf("%s\n", p);//第二个标记"hijklmn"
	//
	//p = strtok(NULL, sep);
	//printf("%s\n", p);//第三个标记"opq"
	//
	//p = strtok(NULL, sep);
	//printf("%s\n", p);//想找第四个标记,但是已经找完了,所以返回NULL

	//正常写法
	char* p = NULL;
	//   初始化部分           判断部分    调整部分
	for (p = strtok(tmp, sep); p != NULL;p = strtok(NULL,sep))
	{
		printf("%s\n",p);
	}

	return 0;
}

十、strerror函数的使用

1.函数基本信息

作用:可以把参数部分(错误码)对应的错误信息的字符串地址返回来

1.在C语言的实现中,库函数的使用,调用如果失败的话,会记录错误码,错误码是一个整数,每一个错误码对应一个信息

2.这些错误码会被记录在一个C语言中的全局变量errno中 - errno的使用需要头文件<errno.h>
3.在不同系统和C语言的标准库的实现都规定了一些错误码,一般放在<errno.h>这个头文件中
4.当程序启动的时候,erron是0 - 这个错误码的意思是“没有错误”

注:由于只有一个errno,所以要及时检测,否则容易被覆盖

char* strerror(int errnum) - 注:errnum为错误码

直接翻译错误码对应信息

int main()
{
	int i = 0;
	for (i = 0; i < 100; i++)
	{
		char* p = strerror(i);
		printf("%d:%s\n",i, p);
	}

	return 0;
}

2.函数的使用

正常使用

int main()
{
	//C语言中打开文件的代码 - fopen
	//这个意思是用"r"(读)的方式,打开"text.txt"文件
	//以"r"读的形式去打开,若文件不存在,则打开失败
	FILE* pf = fopen("test.txt", "r");//打开成功会返回一个FILE类型的指针,打开失败会返回NULL
	if (pf == NULL)
	{
		//printf("%s\n", strerror(errno));//No such file or directory - 没有这个文件或者文件夹
		perror("喵爷");//这个perror也可以打印错误信息,perror相当于printf+strerror的组合
		return 1;      //注:perror需要头文件<stdio.h>
	}

	return 0;
}

perror - 需要头文件<stdio.h>
专门打印错误信息,且可以在()里面自定义打印信息,会打印在错误信息前面
void perror(const char* str)

喵爷镇楼

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值