【C语言】一些库函数的介绍

0.前言

如果觉得这篇博客不错的话,点个赞再走吧😊

这篇博客主要讲的是一些较为常见且实用的字符串函数。

C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。

字符串常量 适用于那些对它不做修改的字符串函数.

int main()
{
	//字符数组
	char arr[] = "asdfghh";
	//常量字符串
	char* p = "asdfghh";
	return 0;
}

1.求字符串长度

strlen

1.1 函数介绍

strlen

size_t strlen ( const char * str );

这个函数非常常见,就是求一下字符串的长度。
从第一个字符开始计数,只要找到’\0’就停止计数。

int main()
{
	char arr[] = "abcd";
	printf("%d\n", strlen(arr));//结果为4
	return 0;
}

1.2 注意事项

  1. 字符串已经 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’ )
  2. 参数指向的字符串必须要以 ‘\0’ 结束
  3. 注意函数的返回值为size_t,是无符号的( 易错 )

1.3 经典例子
例一: 注意函数的返回值为size_t,是无符号的( 易错 )

int main()
{
	const char*str1 = "abcdef";
	const char*str2 = "bbb";
	if(strlen(str2)-strlen(str1)>0)
	{
		printf("str2>str1\n");
	} 
	else
	{
		printf("srt1>str2\n");
	}
	return 0;
}

这里打印出来的是str2 > str1,因为虽然str1的长度为6,str2的长度为3,但是strlen函数返回值为size_t类型的,也就是无符号数,所以在比较的时候补码转换出来的数是3 > 6的,结果也就跟我们平常相像中的不一样。

例二:参数指向的字符串必须要以 ‘\0’ 结束

int main()
{
	char arr1[] = { 'a', 'b', 'c' };
	char arr2[] = "abc";
	printf("%d\n", strlen(arr1));//打印一个未知数
	printf("%d\n", strlen(arr2));//打印3
	return 0;
}

上面arr1的字符串数组没有’\0’,所以strlen当找到字符c之后找不到’\0’,就会在内存中继续寻找,直到在内存中找到了’\0’。这时候结果就是一个未知数。

例三:字符串已经 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’ )

int main()
{
	char arr[] = "qwert \0awer";
	printf("%d\n", strlen(arr));//结果是6而不是11
	return 0;
}

1.4 模拟实现

int my_strlen(char* str)
{
	assert(str);//这是断言str不为空指针

	int count = 0;
	while (*str++)
	{
		count++;
	}
	
	return count;
}

int main()
{
	char arr[] = "awedf";
	printf("%d\n", my_strlen(arr));
	return 0;
}

2. 长度不受限制的字符串函数

strcpy

2.1.1 函数介绍
字符串拷贝函数

strcpy

char* strcpy(char * destination, const char * source )

字符串是不能用等号直接赋值的,所以得要用strcpy这个函数。

这个函数将source这个字符串拷贝到了destination这个字符串中。并且会将’\0’拷到其中。

2.2.1 注意事项

  1. 源字符串必须以 ‘\0’ 结束。
  2. 会将源字符串中的 ‘\0’ 拷贝到目标空间。
  3. 目标空间必须足够大,以确保能存放源字符串。
  4. 目标空间必须可变

2.3.1 经典例子
例一:会将源字符串中的 ‘\0’ 拷贝到目标空间。
在这里插入图片描述
例二:源字符串必须以 ‘\0’ 结束。
在这里插入图片描述
例三:目标空间必须足够大,以确保能存放源字符串。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这样的代码运行起来会直接崩掉。但是编译器还是会将长的字符串拷贝到短的字符串中。

例四:destination必须可变
在这里插入图片描述
在这里插入图片描述
这样也会崩掉,因为常量字符串是无法被修改的。

2.4.1 模拟实现

//函数的返回值为目标空间的起始地址
char* my_strcpy(char* destination, const char* source)
{
	assert(destination && source);

	char* res = destination;
	while (*destination++ = *source++);

	return res;
}
int main()
{
	char arr1[] = "asqwerref";
	char arr2[] = "asdf";
	printf("%s\n", my_strcpy(arr1, arr2));
	return 0;
}

这里可以将返回值给void,如果这样的话打印的时候就不能跟上面一样了,就只能打印arr1。

strcat

2.1.2 函数介绍
字符串追加函数

strcat

char * strcat ( char * destination, const char * source );

这个函数将source字符串的内容追加到了destination的后面。也会将’\0’放到最后。
在这里插入图片描述

2.2.2 注意事项

  1. 源字符串必须以 ‘\0’ 结束。
  2. 目标空间必须有足够的大,能容纳下源字符串的内容。
  3. 目标空间必须可修改。
  4. 自己给自己追加字符串

2.3.2 经典例子
例一:源字符串必须以 ‘\0’ 结束。
在这里插入图片描述

例二:目标空间必须有足够的大,能容纳下源字符串的内容。
在这里插入图片描述
跟上面的strcpy一样,也会崩掉。

例三:目标空间必须可修改。
在这里插入图片描述
也跟strcpy一样。

例四:自己给自己追加字符串
在这里插入图片描述
程序会崩掉,原因如下:
在这里插入图片描述

2.4.2 模拟实现

char* my_strcat(char* destination, const char* source)
{
	assert(destination && source);

	//返回目标空间的地址
	char* res = destination;
	//找到目标空间中的\0
	while (*destination)
	{
		destination++;
	}
	//追加
	while (*destination++ = *source++)
	{
		;
	}
	return res;
}
int main()
{
	char arr1[20] = "bad";
	char arr2[] = "asdf";
	printf("%s\n", my_strcat(arr1, arr2));
	return 0;
}

strcmp

2.1.3 函数介绍
字符串对比函数

strcmp

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

在这里插入图片描述
这里二者对比的时候比的是每一个的字符的大小,如果第一个字符相等,则继续往后比,直到不相等或者同时到了\0为止。如果前一个字符比后一个字符大,返回大于零的数,小则返回小于零的数。

2.2.3 注意事项

  1. 第一个字符串大于第二个字符串,则返回大于0的数字
  2. 第一个字符串等于第二个字符串,则返回0
  3. 第一个字符串小于第二个字符串,则返回小于0的数字

2.3.3 经典例子
小于
在这里插入图片描述
这里a和b先比,直接出结果,a的ASCII比b小就返回小于0的数。

相等
在这里插入图片描述

2.4.3 模拟实现

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);

	while (*str1 == *str2)
	{
		//二者是否同时到达\0
		if (*str1 == '\0')
		{
			return 0;
		}

		str1++;
		str2++;
	}
	
	//这里标准规定是返回大于或小于0的数,没有说一定要返回-1或者1
	return *str1 - *str2;
}


int main()
{
	char arr1[] = "abcedf";
	char arr2[] = "cbcedf";
	int ret = my_strcmp(arr1, arr2);
	printf("%d\n", ret);

	return 0;
}

3. 长度受限制的字符串函数介绍

strncpy

3.1.1 函数介绍

strncpy

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

3.2.1 注意事项

  1. 拷贝num个字符从源字符串到目标空间。
  2. 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

3.3.1 经典例子
例一:拷贝num个字符从源字符串到目标空间。

在这里插入图片描述

例二:如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
在这里插入图片描述
这里拷贝6个字符,因为arr2只有bnm\0,所以拷贝时会再加两个\0。

strncat

3.1.2 函数介绍

strncat

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

3.2.2 注意事项

  1. 追加的时候先找destination的\0,然后再追加
  2. 追加n个字符,并且会在最后加上\0
  3. n若大于source长度,则追加时只会加一个\0,然后就不再追加。

3.3.2 经典例子
例一:追加的时候先找destination的\0,然后再追加
在这里插入图片描述
例二:追加n个字符,并且会在最后加上\0
在这里插入图片描述
例三:n若大于source长度,则追加时只会加一个\0,然后就不再追加。
在这里插入图片描述

strncmp

3.1.3 函数介绍

strncmp

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

3.2.3 注意事项
于strcmp一样,只不过比较的长度规定了
3.3.3 经典例子
在这里插入图片描述
在这里插入图片描述

4. 字符串查找

strstr

4.1 函数介绍

strstr

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

这个函数是用来找str1中str2第一次出现的位置,并返回出现位置的首字符的地址。如果没找到,则返回NULL。

在这里插入图片描述

4.2 模拟实现

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

	char* src = str1;
	while (*src)
	{
		//每次记住str1内部找的首元素
		s1 = src;
		//如果首字符相等了
		if (*s1 == *s2)
		{
			s1++;
			s2++;
			//每个字符都判断一次
			while (*s1 == *s2)
			{
				s1++;
				s2++;
			}
			//除了循环之后无非两种情况
			//第一种就是匹配成功就返回了字符串中的首位置
			if (*s2 == '\0')
				return src;
			//第二种就是匹配失败
			else
				s2 = str2;
		}
		//到这里就是某一趟没找到,就要将str1回溯到匹配的首字符的下一个位置
		src++;
	}
	//到这就是str1都走完了还没找到
	return NULL;
}

int main()
{
	char arr1[] = "abcdefgh";
	char arr2[] = "bcd";

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

	return 0;
}

5. 字符串截断

strtok

5.1 函数介绍

strtok

char * strtok ( char * str, const char * sep );
  1. sep参数是个字符串,定义了用作分隔符的字符集合
  2. str指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
  3. strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
  4. strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
  5. strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
  6. 如果字符串中不存在更多的标记,则返回 NULL 指针。

5.2 使用示例

在这里插入图片描述
但是一般不这么用,这样用起来太麻烦了。
方便一点的是下面这种方式。
在这里插入图片描述

6. 错误信息报告

strerror

6.1 函数介绍

strerror

char * strerror ( int errnum );

这个函数会返回错误码(errnum)所对应的错误信息的首字符的地址。

每一个数字对应一个错误信息。
看例子:
在这里插入图片描述
C语言库函数调用失败的时候,会将失败的错误码放在errno这个变量中。
当我们想要知道调用库函数的时候发生了什么错误,就可以将errno中的错误码转换为错误信息。
errno的头文件就是errno.h
比如说:
在这里插入图片描述

字符操作

is****

下面的这些函数都是判断某一个字符是什么的函数

iscntrl 任何控制字符
isspace 空白字符:空格‘ ’,换页‘\f’,换行’\n’,回车‘\r’,制表符’\t’或者垂直制表符’\v’
isdigit 十进制数字 0~9
isxdigit 十六进制数字,包括所有十进制数字,小写字母af,大写字母AF
islower 小写字母a~z
isupper 大写字母A~Z
isalpha 字母az或AZ
isalnum 字母或者数字,az,AZ,0~9
ispunct 标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph 任何图形字符
isprint 任何可打印字符,包括图形字符和空白字符

这些函数有什么用呢?
就拿大写字母转小写来说吧:
在这里插入图片描述

7. 内存操作函数

7.1 memcpy

1.1 函数介绍

memcpy

void * memcpy ( void * destination, const void * source, size_t num );
  1. 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
  2. 这个函数在遇到 ‘\0’ 的时候并不会停下来。
  3. 如果source和destination有任何的重叠,复制的结果都是未定义的。

1.2 函数示例
普通示例:
在这里插入图片描述

1.3 模拟实现
我们再来看一下这个函数的参数:

void * memcpy ( void * destination, const void * source, size_t num );

这两个参数的类型都是void*的,为什么这么设计呢?
因为void*可以接收任何类型的指针,所以当你想使用这个函数的时候,就不需要纠结传参的类型了。但是void*的指针也有一个缺点,就是不能直接解引用。那么如何解决这个问题呢?
我们还可以看到,第三个参数是需要拷贝的字节大小,num个字节挨个复制,结合前面所学,我们就可以知道,只需要将两个指针强制类型转换为char*类型即可。

下面我们就来模拟实现一下。

void* my_memcpy(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	//返回目标空间的首地址
	void* ret = dest;
	//count个字节要复制
	while (count--)
	{
		//拷贝
		*(char*)dest = *(char*)src;
		//拷贝一次就往后挪
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	//返回目标空间首地址
	return ret;
}

但是如果我们想要自己拷贝自己的话,就不能这样用了,因为有内存重叠的部分。在这里插入图片描述

那么这时候就要用到memmove了。

7.2 memmove

2.1 函数介绍

memmove

void * memmove ( void * destination, const void * source, size_t num );
  1. 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
  2. 如果源空间和目标空间出现重叠,就得使用memmove函数处理。

在这里插入图片描述

这样的话就不会出现循环的情况。

2.2 模拟实现

void* my_memmove(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	if (dest > src)
	{
		//如果dest>src就得从后往前拷贝
		while (count--)
		{
			*((char*)dest + count) = *((char*)src + count);
		}
	}
	else
	{
		//dest>src就得从前往后拷贝
		while (count--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	
	return ret;
}

结果就是这样的:
从后往前拷贝:
在这里插入图片描述
从前往后拷贝:
在这里插入图片描述

7.3 memcmp

3.1 函数介绍

memcmp

int memcmp ( const void * ptr1, const void * ptr2, size_t num );

这个函数和strncmp对比的方式是一样的,只不过是可以用别的类型来进行对比,num就是比较的字节数。
这里就直接上例子了:
相同的情况:
在这里插入图片描述
小的情况:
在这里插入图片描述
大的情况:
在这里插入图片描述

7.4 memset

memset

void* memset( void* dest, int c, size_t count );

这个函数就是你给定字节大小(count), 然后从dest开始的地址往后到count的空间的内容改为c。
在这里插入图片描述
这里要注意当我们count可不是元素个数,而是字节数,若记错了会导致意想不到的后果,像这样:

在这里插入图片描述

到此结束。

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

先搞面包再谈爱

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

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

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

打赏作者

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

抵扣说明:

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

余额充值