字符串函数和内存函数(上)- (strlen、长度受限制字符串函数strcpy、strcat、strcmp、长度受限制字符串函数strncpy、strncat、strncmp, 分别模拟实现 )

目录

一、strlen求字符串长度

1. 函数的返回值为size_t,是无符号的( 易错 )

 2.模拟实现strlen函数 (三种方法)

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

1.strcpy - 拷贝字符串

1)使用

 2)会将源字符串中的 '\0' 拷贝到目标空间。所以源字符串里必须要有\0,否则没法正确拷贝。

1.2.模拟实现strcpy

 1.3.所有字符串函数需要注意的点!!

1)目标空间必须足够大        错误示范

 2)目标空间必须可变。        错误示范

 2.strcat 追加字符串

2.1使用 

 2.2从目的字符串的\0开始追加,同时把源字符串末尾的\0也追加进去了

2.3模拟实现strcat

 2.4strcat自己追加自己?不行!!

3. strcmp - 比较字符串内容是否相同(对应的字符比较)

3.1错误案例 - 在比较用户输入密码的时候很容易犯这种错误

 3.2使用

 3.3模拟实现strcmp

 4.总结

三、长度受限制的字符串函数介绍

1.strncpy 

 1.2. 如果空间足够,但 拷贝的长度 > 源字符长度 ,默认补\0,拷贝上去

2.strncat

2.1使用

 2.2追加超过自身长度字符时,追加完自己所有的字符之后加上\0,到此为止了,不会再追加了

2.3strncat可以自己追加自己

 3.strncmp


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

一、strlen求字符串长度

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

1. 函数的返回值为size_t,是无符号的( 易错 )

int main()
{
	if (strlen("abcdef") - strlen("bbb") > 0)//3-6=-2 >0 不成立,为什么还会是 >
	{
		printf(">\n");
	}							
	else
	{
		printf("<=\n");
		//printf("%u\n", strlen(str2) - strlen(str1));//4294967293

	}
	//最后输出 > ,因为strlen()函数的返回值是 size_t(无符号整型)
	//所以求出的-3 当作无符号整型来看,是一个特别大的正数
	//10000000 ...00000011
	//11111111 ...11111100
	//11111111 ...11111101 - -3的补码
	
	//11111111 ...11111101 看作无符号整型,值为4294967293 ,故输出的是 >
	return 0;
}

 2.模拟实现strlen函数 (三种方法)

//模拟实现strlen
#include<assert.h>
//1.计数器(创建count变量)
int my_strlen_count(const char* str)//因为只是计算字符串长度,不希望指针指向的内容改变,所以加上const安全
{
	assert(str);//断言str不为空指针时,才可以使用,否则警告

	int count = 0;
	while (*str != '\0')//解引用指针如果不是\0则计数,否则跳出循环
	{
		count++;
		str++;
	}
	return count;
}
//2.递归 - (如果题目要求不创建临时变量)
int my_strlen_digui(const char* str)
{
	assert(str);//断言str不为空指针时,才可以使用,否则警告

	if (*str != '\0') //没有遇到\0,1+ 函数(指针后移一位)
	{
		return 1+my_strlen_digui(str + 1);
	}
	else//遇到\0,返回0
	{
		return 0;
	}
}
//3.指针-指针 (求长度)
int my_strlen_point(const char* str)
{
	assert(str);//断言str不为空指针时,才可以使用,否则警告
	char* p = str;
	while (*str)//当*str不为\0时,str一直往后走,直到\0结束
	{
		str++;
	}
	return str - p;//指针str此时指向字符串结束标志\0,p指向字符串起始位置,
	//str - p得到指针之间的距离,也就是字符串的长度

}
int main()
{
	char arr[] = "bit";
	
	printf("%d\n", my_strlen_count(arr));
	printf("%d\n", my_strlen_digui(arr));
	printf("%d\n", my_strlen_point(arr));

	return 0;
}

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

1.strcpy - 拷贝字符串

char* strcpy ( char * destination , const char * source );
  • 源字符串必须以 '\0' 结束。
  • 会将源字符串中的 '\0' 拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变。

1)使用

#include<string.h>
int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = { 0 };
	strcpy(arr2, arr1);//将 arr1 的内容,拷贝到 arr2 中
	printf("%s\n", arr2);//abcdef

	return 0;
}

 2)会将源字符串中的 '\0' 拷贝到目标空间。所以源字符串里必须要有\0,否则没法正确拷贝。

int main()
{
	char arr1[] = "abc\0def";//在abcdef中加了\0,观察拷贝了什么
	//char arr1[] = { 'a','b','c' };//err,源字符串没有\0,当拷贝时,找不到\0,没法正确结束,会一直拷贝,直到找到\0为止
	//这里输出的结果就是 abc烫烫?S?根X
	char arr2[20] = "xxxxxx";
	strcpy(arr2, arr1);//将 arr1 (包含\0)前的内容,拷贝到 arr2 中

	printf("%s\n", arr2);//abc
	//拷贝之后:数组里的内容是 abc\0xx
	//但是因为打印的字符串%s,所以到\0之前就结束了,故指看的了abc
	return 0;
}

1.2.模拟实现strcpy

//模拟实现strcpy
//strcpy返回的目标空间的起始地址(所以是char*)
char* my_strcpy(char* dest, const char* src)//源空间的内容是不能改变的,所以加上const防止被篡改
{
	assert(dest && src);//断言,当dest和src都是有效指针才能使用,否则(空指针)无法使用

	char* ret = dest;//记录dest起始地址
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[] = "hehe";
	char arr2[20] = { 0 };

	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);
	printf("%s\n", my_strcpy(arr2, arr1));
	//因为返回值是char*,可以直接打印这个函数结果,可以不创建变量存起来

	return 0;
}

 1.3.所有字符串函数需要注意的点!!

1)目标空间必须足够大
        错误示范

int main()
{
	char arr1[] = "abcdef";//7个字符
	char arr2[3] = { 0 };//3个空间
	strcpy(arr2, arr1);//将7个字符放入3个空间,根本不够,会报错
	printf("%s\n", arr2);

	return 0;
}

 2)目标空间必须可变。
        错误示范


int main()
{
	char* p = "abcdefgh";//p指向的是一个常量字符串,空间是不可变的
	char arr2[] = "hehe";
	strcpy(p, arr2);//err.欲把arr2的内容,放入一个不可变的空间里,是行不通的
	return 0;
}

 2.strcat 追加字符串

char * strcat ( char * destination , const char * source );
  • 源字符串必须以 '\0' 结束。
  • 从目的字符串的\0开始追加(覆盖掉\0)
  • 目标空间必须有足够的大,能容纳下源字符串的内容。
  • 目标空间必须可修改。
  • 字符串自己给自己追加,如何?- 死循环

2.1使用 


int main()
{
	char arr1[20] = "hello ";
	char arr2[] = "world";

	strcat(arr1, arr2);//将 arr2 的内容,追加到 arr1 里
	printf("%s\n", arr1);//hello world

	return 0;
}

 2.2从目的字符串的\0开始追加,同时把源字符串末尾的\0也追加进去了

说明:strcat,源目字符串数据里必须都要有\0
目的字符串\0,决定了追加开始的位置;
源字符串的\0决定了追加结束的位置


int main()
{
	char arr1[20] = "hello \0xxxxxxx";//后面加上\0xxxxxxx方便测试和观察
	char arr2[] = "world";

	strcat(arr1, arr2);//
	printf("%s\n", arr1);//hello world
	//追加后,目的数组arr1的元素应该为:hello world\0xx
	//打印字符串,得到hello world
	return 0;
}

 

2.3模拟实现strcat

//模拟实现strcat
char* my_strcat(char* dest, const char* src)//源字符串是不能被修改的,加上const
{
	assert(dest && src);//断言,两个指针为有效指针

	char* ret = dest;//记录dest的起始位置
	//先让指针找到目标字符串的第一个\0
	while (*dest)//直到dest指向\0结束循环
	{
		dest++;
	}
	//追加(strcpy拷贝复制源字符串)
	while (*dest++ = *src++)
	{
		;
	}

	return ret;
}
int main()
{
	char arr1[20] = "hello ";
	char arr2[] = "world";

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

 

 2.4strcat自己追加自己?不行!!

    虽然在这个编译器,没有问题,但无法保证在其他编译器上不会出现问题,尽量不要使用这个函数
   若要自己追加自己可以使用strncat,稍微安全一些


int main()
{
	char arr1[20] = "bit";
	strcat(arr1, arr1);

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

	return 0;
}

3. strcmp - 比较字符串内容是否相同(对应的字符比较)

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

第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字
那么如何判断两个字符串? - strcmp

3.1错误案例 - 在比较用户输入密码的时候很容易犯这种错误


int main()
{
 //比较两个字符串内容的时候,不能用==,应该用strcmp
	if ("abcdef" == "qwertyu")//这里比较的是首元素地址,不是字符串的内容
	{							//这里比较的是a和q地址
		;
	}
	return 0;
}

 3.2使用

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

	int ret = strcmp(arr1, arr2);
	//比较对应字符的ASCII码的大小,相等往后比较,直到某个字符不相等,开始比较
	//大于返回大于0的数,小于返回小于0的数,相等返回0
	printf("%d", ret);//c < q ,返回小于0的数 -1
}

 3.3模拟实现strcmp

//模拟实现strcmp
int my_strcmp(const char* str1,const char* str2)//两个字符串比较,两个都不能改变,所以都加上了const
{
	assert(str1 && str2);//断言,两个指针有效才能使用
	while (*str1 == *str2)
	{
		if (*str1 == '\0')//两个字符串对应字符比较完之后都相等,一直到\0的时候,返回0
		{
			return 0;
		}
		str1++;
		str2++;
	}
	
	return *str1 - *str2;//如果大于相减的数就是个正数,相反就是个负数

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

	int ret = my_strcmp(arr1, arr2);
	
	printf("%d", ret);//c < q ,返回小于0的数 -1
}

 4.总结

strcpy,strcat,strcmp这三个都是长度不受限制的函数,使用时容易出错
所以可以使用长度受限制的函数:strncpy,strncat,strncmp

例如:即使目标空间不够放入这么多个字符,也报了警告,但依然把多余的字符拷贝进去了


int main()
{
	char arr1[] = "abcdef";//7个字符
	char arr2[5] = { 0 };//5个空间
	strcpy(arr2, arr1);//将7个字符放入5个空间,根本不够,会报错
	printf("%s\n", arr2);

	return 0;
}

三、长度受限制的字符串函数介绍

1.strncpy 

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

因为有了长度限制,在编写的时候,可能会多想一下拷贝几个符,相对strcpy还是安全一些;
但是硬要超过限度拷贝,那还是会出错的。(报警并且强制拷贝)

1.如果 拷贝的长度 > 目标空间(不够),强硬拷贝会出错,请三思后行
2.如果空间足够,但 拷贝的长度 > 源字符长度 ,默认补\0,拷贝上去


int main()
{
	char arr1[] = "abcdef";//7个字符
	char arr2[5] = { 0 };//5个空间
	//char arr2[5] = "xxxxx";//可以观察是否只拷贝了3个
	strncpy(arr2,arr1,3);
	//strncpy拷贝,多了一个参数,拷贝几个字符
	//拷贝3个字符,就拷贝arr1的abc
	printf("%s\n", arr2);

	return 0;
}

 1.2. 如果空间足够,但 拷贝的长度 > 源字符长度 ,默认补\0,拷贝上去
 

虽然最终打印的字符串结果没有变化,但是,调试可以看到这个细节

int main()
{
	char arr1[] = "abcdef";//7个字符
	char arr2[20] = "xxxxxxxxxxxxxxxxxxx";//20个空间
	strncpy(arr2, arr1, 10);
	//2.如果空间足够,但 拷贝的长度 > 源字符长度 ,默认补\0,拷贝上去
	//a b c d e f \0 \0 \0 \0 x x x x x x x x x
	printf("%s\n", arr2);
	//打印字符串,得到abcdef
	return 0;
}

 

2.strncat

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

2.1使用

int main()
{
	char arr1[20] = "hello \0xxxxx";
	char arr2[] = "abcdef";
	strncat(arr1, arr2, 3);//将arr2的3个字符,追加到arr1里
	printf("%s\n", arr1);//hello abc
	//数组里是:hello abc\0xx\0\0\0\0\0\0\0\0
	// 追加完abc三个字符之后,会补上一个\0
	//打印字符串得到hello abc
	return 0;
}

 2.2追加超过自身长度字符时,追加完自己所有的字符之后加上\0,到此为止了,不会再追加了

虽然最终打印的字符串结果没有变化,但是,调试可以看到这个细节

int main()
{
	char arr1[20] = "hello \0xxxxxxxxxxxxx";
	char arr2[] = "abcdef";
	strncat(arr1, arr2, 10);//追加10个字符(超过arr2的范围)
	printf("%s\n", arr1);//hello abcdef
	//数组里是:hello abcdef\0xxxxxxx
	// 追加超过自身长度字符时,追加完自己所有的字符之后加上\0,到此为止了,不会再追加了
	//打印字符串得到hello abc
	return 0;
}

 

 

2.3strncat可以自己追加自己

//strcat不能自己给自己追加,有可能会修改掉字符串最后的\0,可能导致死循环
//如果要自己给自己增加,可以使用strncat

 3.strncmp

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

//**********  strncmp
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abc";
	int ret = strncmp(arr1, arr2, 3);//比较 arr1 和 arr2 前3个字符
	printf("%d\n", ret);

	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值