字符函数和字符串函数

目录

strncpy

 strncat

strncmp 

 strstr

 strtok

 strerror

memcpy 


上篇博客中,我学到的strcpy,strcat,strcmp是长度不受限制的字符串函数。比如strcpy,它会把源字符串中的数据无论长度大小全部拷贝到目标字符串中,直到遇到\0为止。

int main()
{
	char arr[4] = { 0 };
	strcpy(arr, "hello world");
	return 0;
}

可以看到,arr数组只有4个字节的空间,是没有能力存放”hello world“的,而事实上,strcmp这个函数还是把 ”hello world“给拷贝到arr里面去了。虽然最后打印出来了,但是有警告出现。是因为造成了越界访问。这样是非常不安全的!!!

考虑到长度不受限制的函数会有一些安全隐患,接下来给大家介绍几个相对安全的长度受限制的字符串函数。

strncpy

这个函数相比strcpy多了个参数num。能够限制拷贝到目标字符串的长度。

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "hello world";
	strncpy(arr1, arr2, 5);
	printf("%s\n", arr1);
	return 0;
}

 

如果要拷贝的长度大于源字符串的长度用\0代替。

strncpy的模拟实现: 

char* my_strncpy(char* dest, const char* src, size_t num)
{
	assert(dest);
	assert(src);
	size_t count = 0;
	char* ret = dest;
	for (count = 0; count < num; count++)
	{
		*dest++ = *src++;
		if (*src=='\0')
		{
			*dest++ = *src++;
			return ret;
		}
	}
	return ret;
}
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "shiyanan";
	my_strncpy(arr1, arr2,3);
	printf("%s\n", arr1);
}

 strncat

strncat用法和strcat用法类似,加了个参数num

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[20] = "hello ";
	char arr2[] = "world";
	strncat(arr1, arr2, 3);
	printf("%s\n", arr1);
    return 0;
}

strcat和strncat都是在目标字符串找到\0,然后在\0后面追加的。同时strcat把源字符串后面隐藏的\0追加上去,strncat在追加完成后补上\0

int main()
{
	char arr1[20] = "hello\0world ";
	char arr2[] = "abcdef";
	strncat(arr1, arr2, 3);
	printf("%s\n", arr1);
    return 0;
}

 strncat的模拟实现

#include<assert.h>
char* my_strncat(char* dest, const char* src, size_t num)
{
	assert(dest);
	assert(src);
	size_t count = 0;
	char* ret = dest;
	while (*dest++);
	dest--;
	for (count = 0; count < num; count++)
	{
		if (*src == '\0')
		{
			*dest = *src;
			return ret;
		}
		*dest++ = *src++;
	}
	*dest = '\0';//在末尾补上\0
	return ret;
}
int main()
{
	char arr1[20] = "hello\0world";
	char arr2[] = "abcdef";
	my_strncat(arr1, arr2, 3);
	printf("%s\n", arr1);
	return 0;
}

 

strncmp 

strncmp用法和strcmp用法类似,加了个参数num

int main()
{
	char arr1[20] = "shiyanan";
	char arr2[] = "baiyang";
	int ret=strncmp(arr1, arr2, 2);
	if (ret > 0)
	{
		printf(">\n");
	}
	else if (ret == 0)
	{
		printf("==\n");
	}
	else
	{
		printf("<");
	}
	return 0;
}

 strstr

查找子串的一个函数

 返回指向 str1 中第一次出现的 str2 的指针,如果 str2 不是 str1 的一部分,则返回空指针。

通俗点说就是,如果str1指向的字符串的内容包含str2指向字符串的内容的话,返回str1所指向字符串与str2所指向字符串相同部分的起始地址。如果如果str1指向的字符串的内容不包含str2指向字符串的内容的话,返回NULL空指针。

举个例子

int main()
{
	char email[] = "baiyang@qq.com";
	char arr[] = "yang";
	char* ret = strstr(email, arr);
	if (ret==NULL)
	{
		printf("子串不存在\n");
	}
	else
	{
		printf("%s\n", ret);
	}
	return 0;
}

strstr的模拟实现

要注意的是:strstr这个函数的逻辑是,如果有以下俩个字符串"cbbacd"和"bacd",函数是拿”cbbacd“和"bacd"的首字符从c,b进行比较,不相等,再返回到首字符(刚刚进行比较的字符)后面一个字符,也就是拿cbbacd的第二个字符b与bacd首字符b比较。相等,再拿第cbbacd的第三个字符b与bacd的第二个字符a进行比较。不相等,则返回到”cbbacd“第二个字符后面的第三个字符与bacd进行比较。依次类推,要遍历完整个字符串。

通俗点说就是str1所指向的字符串中的每个字符都要当作起始位置和str2所指向的字符串进行比较,直到\0为止。

 char* my_strstr(const char* str1,const char* str2)
{
	assert(str1);
	assert(str2);
	const char* p = str1;
	const char* s1 = str1;
	const char* s2 = str2;
	while (*p)
	{
		s1 = p;
		s2 = str2;
		while (*s1 ==*s2 && *s1 != '\0' && *s2 != '\0')
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return (char*)p;
		}
		p++;
	}
	return NULL;
}

 

 strtok

切割字符串

 该函数使用时要注意:

1.delimiters参数是分隔符的意思,在这里是个字符串,定义了用作分隔符的字符集合。

2.第一个参数str指定一个字符串,它包含了0个或者多个由delimiters字符串中的一个或者多个分隔符分割的标记。

3.strtok函数会改变操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可以修改。

4.strtok函数找到str中的下一个标记(也就是delimiters这个字符串中的分隔符),并将其用\0结尾,返回一个指向这个标记的指针。

5.strtok函数的第一个参数不为NULL,函数将找到str中的第一个标记,strtok将保存它在字符串中的位置。

6.strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,找到下一个标记。

7.如果字符串中不存在更多的标记,则返回NULL指针。

int main()
{
	char email[] = "shiyanan@qq.com";
	char del[] = "@.";
	char cp[30] = { 0 };
	strcpy(cp, email);//拷贝一份email用于分割。
	char* ret=strtok(cp, del);
	printf("%s\n", ret);
	ret=strtok(NULL, del);
	printf("%s\n",ret);
	ret=strtok(NULL,del);
	printf("%s\n", ret);
	printf("%s\n", cp);//给大家康康,这个函数确实改变了源字符串
	return 0;
}

利用for循环的性质简化代码量

int main()
{
	char email[] = "shiyanan@qq.com";
	char del[] = "@.";
	char cp[30] = { 0 };
	strcpy(cp, email);//拷贝一份email用于分割。
	char* ret = NULL;
	for (ret = strtok(cp, del); ret != NULL; ret = strtok(NULL, del))//利用for循环的性质简化代码
	{
		printf("%s\n", ret);
	}
	return 0;
}

 strerror

错误报告函数,返回错误码所对应的错误信息。

C语言的库函数在执行失败时,都会设置错误码,例如0,1,2,3……

int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	return 0;
}

memcpy 

内存拷贝:把src所指向的内存空间的内容拷贝到dest所指向的内存空间中。

1.(Copies the values of num bytes from the location pointed to by source directly to the memory block pointed to by destination.)将 num 字节值从源指向的位置直接复制到目标指向的内存块。

2.(The underlying type of the objects pointed to by both the source and destination pointers are irrelevant for this function; The result is a binary copy of the data.)源指针和目标指针所指向的对象的基础类型与此函数无关;结果是数据的二进制副本。

3.(The function does not check for any terminating null character in source - it always copies exactly num bytes.)该函数不检查源中是否有任何终止空字符 - 它始终精确地复制数字字节。

4.(To avoid overflows, the size of the arrays pointed to by both the destination and source parameters, shall be at least num bytes, and should not overlap (for overlapping memory blocks, memmove is a safer approach).)为避免溢出,目标参数和源参数所指向的数组的大小应至少为 num 个字节,并且不应重叠(对于重叠的内存块,memmove 是一种更安全的方法)。

另外memcpy拷贝的是俩块独立空间的数据,它不能自己拷贝自己。

函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7 };
	int arr2[20] = { 0 };
	memcpy(arr2, arr1, 28);
	return 0;
}

 memcpy的模拟实现

void* my_memcpy(void* dest, void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;
	while (num--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值