字符函数和字符串函数

常见字符串的库函数的使用和注意事项

求字符串长度

strlen函数
我们都知道C语言没有字符串类型,举个例子:char arr[ ]=" abcdef " ; arr作为数组名会被传递到strlen()函数中来计算字符串长度。了解一下strlen函数的基本信息:
在这里插入图片描述
strlen函数的函数声明
在这里插入图片描述
strlen函数的参数和返回类型
但这只是一个普通的字符串,abcdef后面会有‘\0’,但是如果没有‘\0’呢?

#include<stdio.h>
//strlen函数的实现
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	int ret = strlen(arr);
	printf("%d\n", ret);
	return 0;
}

如上代码运行后发现,程序最终会打印一个不是我们意料中的数,这是因为strlen函数以 \0 作为结束标志,strlen函数返回的是 \0 前面字符出现的个数(不包含 \0 )这里注意两个点:
在这里插入图片描述
第二点是使用者尤其会忽略的一个地方,这里再次提醒。
模拟实现strlen函数:

//模拟实现strlen
#include<stdio.h>
#include<assert.h>
size_t my_strlen(const char* str)
{
	int count = 0;
	assert(str != NULL);
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	return 0;
}

字符串拷贝函数strcpy

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

参考如上代码可以知道strcpy函数的基本实现,就是把arr2的内容拷贝到arr1中。但是被拷贝的内容中要有 ‘\0’ , strcpy会把arr2中‘ \0 ’之前的所有内容全部拷贝到arr1中,同时将 ‘ \0 ’也进行拷贝。 如果arr2中没有‘ \0 ’,就可能会导致程序崩溃。strcpy只会寻找‘ \0 ’,如果没有找到,就会一直向后寻找,最终导致访问越界。但是看下面这个例子:

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[20] = { 0 };
	char arr2[10] = { 'a','b','c' };
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

运行一下会发现没有任何问题,这是因为arr2数组被初始化了,虽然只存放了三个元素,但是剩余的空间被初始化成了‘ \0 ’。因此strcpy函数还是可以正常使用的。
目标空间必须足够大,以便确保字符串能存放到目标空间中。

此时就引出了strcpy函数的一个缺陷:strcpy函数不会考虑拷贝会不会导致程序崩溃,只会一股脑进行拷贝,因此此时程序员不能依靠函数本身来进行纠错,此时进行代码编写的时候要格外注意。

目标空间必须是可变的
在这里插入图片描述
这样的代码时错误的,在运行的时候会报错。
strcpy的模拟实现

//strcpy函数
#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* str1, const char* str2)
{
	assert(str1 && str2);
	char a = 0;
	char* ret = str1;
	while (*str1++ = *str2++)
	{
		;
	}
	*str1 = *str2;//拷贝'\0'
	return ret;
}
int main()
{
	char arr1[20] = {0};
	char arr2[20] = "hello,bit";
	my_strcpy(arr1, arr2);

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

字符串追加函数strcat

在这里插入图片描述
基本了解一下strcat的作用:

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

在这里插入图片描述
strcat函数在使用的时候,原字符串必须要有‘ \0 ’,要以‘ \0 ’作为结束标志,目标字符串中也要有‘ \0 ’,从‘ \0 ’向后追加。

目标空间必须足够大,能容纳得下源字符串的内容。

目标空间必须可以修改
模拟实现strcat

//strcat函数
#include<stdio.h>
#include<assert.h>
my_strcat(char* str1, const char* str2)
{
	assert(str1 && str2);
	char* ret = str1;
	while (*str1 != '\0')
	{
		str1++;
	}
	/*while (*str2 != '\0')
	{
		*str1 = *str2;
		str1++;
		str2++;
	}
	*str1 = '\0';*/
	while (*str1++ = *str2++)
	{
		;
	}
	return ret;
}

int main()
{
	char arr1[20] = "hello";
	char arr2[] = " bit";
	//strcat(arr1, arr2);
	my_strcat(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

字符串比较函数strcmp

在这里插入图片描述
该函数返回int类型
第一个字符串大于第二个字符串,返回大于0的数字
第一个字符串等于第二个字符串,返回0
第一个字符串小于第二字符串,返回小于0的数字
在这里插入图片描述
注意,strcmp比较的不是字符串的长度,比较的方式是对每个元素的ASCII码值进行比较,比如上面的例子中,arr1中的‘ a ’与arr2中的 ‘ a ’相同,一直到arr1中的‘c’与‘q’进行比较,后者的ASCII码值比前者的ASCII码值大,因此输出-1.strcmp 的比较会一直进行到不同或者都遇到‘ \0 ’。
模拟实现strcmp

#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 != '\0' && *str2 != '\0'&&*str1==*str2)
	{
		str1++;
		str2++;
	}
	return *str1 - *str2;
}
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abc";
	int ret = my_strcmp(arr1, arr2);
	printf("%d\n", ret);
	return 0;
}

以上所有的函数都是长度不受限制的字符串函数

长度受限制的字符串函数

strncpy函数

在这里插入图片描述
在这里插入图片描述
程序的运行基本演示了函数的使用方法和作用:
size_t num表示拷贝字符串的个数,这么做程序就看起来安全了,但这只是相对安全,毕竟你阻止不了一个想写bug的程序员。
看一下下面这个特殊情况:

#include<stdio.h>
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "qwd";

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

如果代码是这样,arr2只有四个字符,但是却要求操作五个字符,当超出操作字符的数量时,会用‘ \0 ’补齐,直到num个。
在这里插入图片描述

strncat函数

在这里插入图片描述
代码实现如下:
在这里插入图片描述
strncat会从‘ \0 ’处进行追加,但是不同于上面的情况,当num越界时:

#include<stdio.h>
int main()
{
	char arr1[20] = "abcde\0xxxxxx";
	char arr2[] = "qwe";
	strncat(arr1, arr2, 5);
	printf("%s\n", arr1);
	return 0;
}

在这里插入图片描述
在这里插入图片描述
经过调试发现,并没有多余的‘ \0 ’。

strncmp函数

在这里插入图片描述
函数参数中的size_t num表示的是比较几个元素。

//strncmp函数
#include<stdio.h>
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcettyu";
	int ret=strncmp(arr1, arr2,4);
	printf("%d\n", ret);
	return 0;
}

strstr函数

在这里插入图片描述
函数的作用是看一个字符串是不是另一个字符串的子串,也就是str2是不是在str1中,如果在返回字符串第一次出现的位置(地址)没有出现,就会返回空指针。 下面来看代码:

#include<stdio.h>
int main()
{
	char arr1[] = "abcdeafgcdefxxxx";
	char arr2[] = "cdef";
	char*p=strstr(arr1, arr2);
	if (p == NULL)
	{
		printf("找不到子串\n");
	}
	else
	{
		printf("%s\n", p);
	}
	return 0;
}

在这里插入图片描述
模拟实现strstr

//strstr函数
#include<stdio.h>
char* my_strstr(char* str1, char* str2)
{
	char* s1 = str1;
	char* s2 = str2;

	char* cur = str1;
	while (*cur)
	{
		s1 = cur;
		s2 = str2;

		while (*s1 == *s2&&*s1&&*s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0');
		{
			printf("找到了\n");
			return cur;
		}
		cur++;
		
	}
	return NULL;
}
int main()
{
	char arr1[] = "abcdeafgcdefxxxx";
	char arr2[] = "cdef";
	char*p=my_strstr(arr1, arr2);
	if (p == NULL)
	{
		printf("找不到子串\n");
	}
	else
	{
		printf("%s\n", p);
	}
	return 0;
}

strtok函数

在这里插入图片描述
sep参数是个字符串,定义了用作分隔符的字符集合。

第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。也就是说,str指向的字符串是sep中的字符分开的。

strtok函数要找到str中的下一个标记,并将其用‘ \0 ’结尾,返回一个指向这个标记的指针。strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般是临时拷贝的内容并且可修改

strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
strtok函数的第一个参数为UNLL,函数将在同一个字符串中被保存的位置开始,查护下一个标记。
如果字符串中不存在更多的标记,则返回NULL指针。
好像有些不好懂,我们来看一下代码:

//strtok函数
#include<stdio.h>
#include<string.h>
int main()
{
	char arr[] = "adffvgb@year.net";
	char buf[30] = { 0 };
	strcpy(buf,arr);
	//strtok函数
	//"@."定义为一个字符串
	const char* str = "@.";
	char* pp = NULL;
	//printf("%s\n",strtok(buf, str));//只找第一个标记
	//printf("%s\n", strtok(NULL, str));//从保存好的位置开始继续往后查找
	//printf("%s\n", strtok(NULL, str));
	for (pp = strtok(buf, str); pp != NULL; pp=strtok(NULL, str))
	{
		printf("%s\n",pp);
	}
	return 0;
}

这里注意,只要含有str内容中的字符,就会进行分割。

strerror函数

全局变量errno(错误码)–>一旦某一个库函数调用失败,就会返回一个错误码,将错误码放进errno中
在这里插入图片描述
返回值为错误码对应错误信息的指针。
在这里插入图片描述
strerror函数和errno的使用(malloc函数是在堆区开辟空间)
在这里插入图片描述

字符转换函数

int tolower ( int c ) ; 函数实现大写转换小写
int toupper ( int c ) ; 函数实现小写转换大写

在这里插入图片描述

内存操作函数

memcpy函数

在这里插入图片描述
参数类型之说以是void*是因为作者在实现memcpy的时候,并不知道使用者会传递一个什么样的参数进去(与qsort函数类似)
在这里插入图片描述
代码基本包含了函数的作用和使用方法,其中count的意思是拷贝的字节数,是一个无符号整型数据。

模拟memcpy函数

void* my_memcpy(void* str1, const void* str2,size_t count)
{
	assert(str1 && str2);
	while (count--)
	{
		*((char*)str1) = *((char*)str2);
		str1 = (char*)str1 + 1;
		str2 = (char*)str2 + 1;
	}
}

但是还是会有一些意外:
在这里插入图片描述
这个程序,我们预期的结果是1 2 1 2 3 4 5 8 9 10,但实际上结果是1 2 1 2 1 2 1 8 9 10,这是因为程序改变了源数组中的内容,数组内部的拷贝发生了重叠,这时就引出了另外一种函数:memmove函数

memmove函数

memmove函数可以实现重叠内存的拷贝
在这里插入图片描述
在这里插入图片描述
但是如果模拟这个函数,还有很多细节要注意:
在这里插入图片描述
以上的这两种拷贝字符串如果使用同一种拷贝顺序,必然会导致拷贝出现问题,综上,memmove函数的模拟实现要分情况。

src是源数组首元素的地址,dest是目标数组的首元素的地址

如果src的地址比dest大,就从前往后进行拷贝。
在这里插入图片描述

如果src的地址dest小,但是二者仍然重叠,就从后往前进行拷贝。
在这里插入图片描述

dest继续向后二者不在重叠的时候,可以从后向前拷贝,也可以从前向后拷贝。

#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* str1, const void* str2,size_t count)
{
	assert(str1 && str2);
	while (count--)
	{
		*((char*)str1) = *((char*)str2);
		str1 = (char*)str1 + 1;
		str2 = (char*)str2 + 1;
	}
}
void*my_memmove(void* str1, const void* str2,size_t count)
{
	if (str1 < str2&&str1<(char*)str2+count)
	{
		//前->后
		assert(str1 && str2);
		while (count--)
		{
			*(char*)str1 = *(char*)str2;
			str1 = (char*)str1 + 1;
			str2 = (char*)str2 + 1;
		}
		
	}
	else if(str1>str2)
	{
		//后—>前
		assert(str1 && str2);
		while (count--)
		{
			*((char*)str1 + count) = *((char*)str2 + count);
		}
	}
}
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[5] = { 0 };
	//my_memcpy(arr2, arr1, 20);
	memmove(arr1 + 2, arr1, 20);//实现重叠内存的拷贝
	return 0;
}

memcmp函数

在这里插入图片描述
对于函数的返回值
在这里插入图片描述
函数的比较是一个字节一个字节进行比较的。

memset函数—内存设置

在这里插入图片描述
dest是数组地址,c是改变的内容,count是改变的字节数,修改的是每个字节,而不是某个字节
在这里插入图片描述
函数的意思就是把内存中的每个字节都改成6。

内容有点多,大家慢慢学习,希望大家收获知识。心之所向,素履以往!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Feng,

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

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

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

打赏作者

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

抵扣说明:

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

余额充值