C语言中常用的库函数(。・∀・)ノ゙嗨

目录

前言:

一:字符串函数:

1.  strlen——求字符串的长度

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

1):strcpy——字符串的拷贝:

  2):strcat——追加字符串

  3):strcmp——字符串之间的比较

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

     1):strncpy——拷贝一定长度的字符串:

2):strncat——追加一定长度的字符串

  3):strncmp——比较一定长度的字符串

  4.字符串的查找:

  1):strstr——查找子字符串

       2):strtok——将字符串拆分为标记

3):strerror——获得字符串的错误信息

5.特殊的字符函数:

        1):字符分类函数:

       2):字符转换函数:

二:内存函数:

         1.  memcpy——内存拷贝函数

  2.  memmove——内存拷贝函数

3.  memset——填充内存的函数

4.  memcmp——比较两个内存的函数

总结:


大家好,曳渔又来了~

希望可以获得大家的点赞和关注

前言:

     大家在编写程序的时候,经常会用到字符和字符串,为了方便操作呢,C语言标准库中呢提供了一系列的关于字符和字符串的函数,今天就让我们来学习一下吧!!!

在下面介绍的函数都需要引用头文件<string.h>

一:字符串函数:

1.  strlen——求字符串的长度

函数的原型:


    

函数的作用:

字符串以'\0'作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包
  含'\0' )

 参数是一个字符指针变量

 参数指向的字符串必须以'\0'结束

注意函数的返回值是size_t,是无符号的

使用strlen函数的注意事项:

咱们演示一下size_t是什么意思

OK 我们可以看到,arr1长度应该是6,arr2的长度应该是5,那么arr2-arr1应该是-1,所以应该是打印arr2<arr1啊,为什么打印arr2>arr1呢,这就是size_t的作用了。6和5都是以无符号形式存储的,当他们相减后得到的-1也应该以无符号形式存储,而-1在内存中以补码形式存在时,存储的数会很大!!!这就是size_t的作用所以,我们要减少写此类的代码!!!

三种模拟实现的方法:

在我们会使用后,我们也要明白它是如何实现的。

第一种:计数器方法

#include<stdio.h>
#include<string.h>
#include<assert.h>
size_t my_strlen1(const char* a)
{
	//计数器
	assert(a);
	int count = 0;
	while (*a)
	{
		++count;
		++a;
	}
	return count;
}
int main()
{
	char arr[] = {"abcdefgh"};
	printf("%zd\n",my_strlen1(arr));
	return 0;
}

第二种:指针-指针

#include<stdio.h>
#include<string.h>
#include<assert.h>
size_t my_strlen2(const char* b)
{
	//指针减指针
	assert(b);
	char* p = b;
	while (*p != '\0')
	{
		++p;
	}
	return p- b;
}
int main()
{
	char arr[] = {"abcdefgh"};
	printf("%zd\n", my_strlen2(arr));
	return 0;
}

第三种:不创建临时变量(递归方法)

#include<stdio.h>
#include<string.h>
#include<assert.h>
size_t my_strlen3(const char* c)
{
	//不创建临时变量
	assert(c);
	if (*c == '\0')
	{
		return 0;
	}
	else
	{
		return 1 + my_strlen3(c+1);
	}
}
int main()
{
	char arr[] = { "abcdefgh" };
	printf("%zd\n", my_strlen3(arr));
	return 0;
}

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

1):strcpy——字符串的拷贝:

函数原型

 函数的作用:

把源字符串拷贝到目标字符串上

   源字符串必须以 '\0' 结束
•  会将源字符串中的 '\0' 拷贝到目标空间
•  目标空间必须足够大,以确保能存放源字符串
•  目标空间必须可修改

使用函数时的特殊情况:

会把'\0'拷贝进去遇到'\0'终止

 

函数的模拟实现:

#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcpy(char* a, const char* b)
{
	assert(a != NULL);
	assert(b != NULL);
	char* c = a;
	while (*a++ = *b++)
	{
		;
	}
	return c;
}
int main()
{
	char arr1[] = { "hello word" };
	char arr2[20] = { 0 };
	
	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

  2):strcat——追加字符串

函数模型:

 函数的作用:

追加函数:在目标字符串后面追加源字符串

  源字符串必须以 '\0' 结束
  目标字符串中也得有 \0 ,否则没办法知道追加从哪里开始
  目标空间必须有足够的大,能容纳下源字符串的内容
  目标空间必须可修改

注意:不能自己追加自己,那样会陷入死循环

函数的模拟实现:

#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcat(char* a, const char* b)
{
	assert(a!=NULL);
	assert(b!=NULL);
	char* c = a;
	while (*a)//先找被添加的\0
	{
		a++;
	}//循环结束*a为\0
	while (*a++ = *b++)
	{
		;
	}
	return c;
}
int main()
{
	char arr1[20] = { "hello " };
	char arr2[20] = { "word" };
	my_strcat(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

  3):strcmp——字符串之间的比较

函数原型:

函数的作用:

 比较函数:比较两个字符串的大小,根据ASCLL值进行比较

比较规则:

  第⼀个字符串大于第二个字符串,则返回大于0的数字
  第⼀个字符串等于第二个字符串,则返回0
  第⼀个字符串小于第二个字符串,则返回小于0的数字

举个例子:

函数的模拟实现: 

#include<stdio.h>
#include<string.h>
#include<assert.h>
int my_strcmp(const char* arr1, const char* arr2)
{
	assert(arr1);
	assert(arr2);
	while (*arr1 == *arr2)
	{
		if (*arr2 == '\0')
		{
			return 0;
		}
		arr1++;
		arr2++;
	}
	return *arr1 - *arr2;
}
int main()
{
	char arr1[] = { "abcdef" };
	char arr2[] = { "abcdgh" };
	printf("%d\n", strcmp(arr1, arr2));
	printf("%d\n", my_strcmp(arr1, arr2));
	return 0;
}

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

     1):strncpy——拷贝一定长度的字符串:

会有人问为什么会有长度受限制的函数,直接用不受限制的不行么,答案是有时候是不可以的因为,不受限制的函数如果目标空间的内存不够大,直到找到源字符串的'\0'才结束的话会造成越界,所以不安全,所以才会出现长度受限制的函数。

函数模型:

函数的作用:

 把拷贝的函数受限制

  拷⻉num个字符从源字符串到⽬标空间
  如果源字符串的⻓度⼩于num,则拷⻉完源字符串之后,在⽬标的后边追加0,直到num个

模拟实现:

#include<stdio.h>
#include<string.h>
//模拟实现strncpy
char* my_strncyp(char* dest, const char* src, int num)
{
	int i = 0;
	char* b = dest;
	for (i = 0;*src!='\0' && i < num; i++)
	{
		*dest = *src;
		dest++;
		src++;
	}
	if (i < num)
	{
		*dest = '\0';
	}
	return b;

}
int main()
{
	char arr1[20] = { "abcdef" };
	char arr2[20] = { "ghjk" };
	char* a = strncpy(arr1, arr2, 4);
	printf("%s\n", a);
	char arr3[20] = { "abcdef" };
	char arr4[20] = { "ghjk" };
	char* b = my_strncyp(arr3, arr4, 5);
	printf("%s\n", b);
	return 0;
}

2):strncat——追加一定长度的字符串

函数模型:

函数的作用: 

追加一定长度的字符

  将字符串的前num个字符追加指向的字符串末尾,再追加⼀个 \0 字符

  如果源字符串的长度小于num时,会再拷贝完成后,在目标字符串后面追加'\0'直到num

函数的实现:

#include <stdio.h>
#include <string.h>
char* my_strncat(char* dest, const char* src, int num)
{
	int i = 0;
	char* a = dest;
	while (*dest)
	{
		dest++;
	}
	for (i = 0; *src!='\0'&&i < num; i++)
	{
		*dest = *src;
		dest++;
		src++;
	}
	if (i < num)
	{
		*dest = '\0';
	}
	return a;
}
int main()
{
	char str1[20] = { "abcdef " };
	char str2[20] = { "ghigklmn" };
	strncat(str1, str2, 10);
	printf("%s\n", str1);
	char str3[20] = { "abcdef " };
	char str4[20] = { "ghigklmn" };
	char* a = my_strncat(str3, str4, 10);
	printf("%s\n", a);
	return 0;
}

  3):strncmp——比较一定长度的字符串

函数模型:

函数的模拟实现: 

#include<stdio.h>
#include<string.h>
#include<assert.h>
int my_strcmp(const char* arr1, const char* arr2)
{
	assert(arr1);
	assert(arr2);
	while (*arr1 == *arr2)
	{
		if (*arr2 == '\0')
		{
			return 0;
		}
		arr1++;
		arr2++;
	}
	return *arr1 - *arr2;
}
int main()
{
	char arr1[] = { "abcdef" };
	char arr2[] = { "abcdgh" };
	printf("%d\n", strcmp(arr1, arr2));
	printf("%d\n", my_strcmp(arr1, arr2));
	return 0;
}

  4.字符串的查找:

  1):strstr——查找子字符串

函数的模型:

函数的作用:

判断是否是子字符串 

  函数返回字符串str2在字符串str1中第⼀次出现的位置

  字符串的比较匹配不包含 \0 字符,以 \0 作为结束标志

函数的模拟实现:

#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strstr(const char* arr1, const char* arr2)
{
	const char* s1 = NULL;
	const char* s2 = NULL;
	const char* cur = arr1;
	if (*arr2 == '\0')
	{
		return (char*)arr1;
	}
	while (*cur)
	{
		s1 = cur;
		s2 = arr2;
		while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return (char*)cur;
		}
		cur++;
	}
	return NULL;
}
int main()
{
	char arr1[] = { "abcdddedefgh" };
	char arr2[] = { "ddede" };
	char* str = strstr(arr1, arr2);
	printf("%s\n", str);
	char* str1 = my_strstr(arr1, arr2);
	printf("%s", str1);
	return 0;
}

       2):strtok——将字符串拆分为标记

函数的模型:

函数的作用: 

 delimiters参数指向⼀个字符串,定义了用作分隔符的字符集合
  第⼀个参数指定⼀个字符串,它包含了0个或者多个由delimiters字符串中⼀个或者多个分隔符分割的标记
  strtok函数找到str中的下⼀个标记,并将其用 \0 结尾,返回⼀个指向这个标记的指针

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

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

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

函数的使用:


3):strerror——获得字符串的错误信息

函数模型:

函数的作用:

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

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

函数应用:

在Windows11+VS2022环境下输出的结果如下:

这里还有一个函数为perror函数,相当于printf+errno

5.特殊的字符函数:

        1):字符分类函数:

这类函数的使用时需要引用ctype.h

       2):字符转换函数:

1. tolower:将参数传进去的大写字母转小写

      int tolower ( int c ); 

2. toupper:将参数传进去的小写字母转大写

      int toupper ( int c );

二:内存函数:

         1.  memcpy——内存拷贝函数

函数模型:

 函数的作用:

  函数从源函数开始复制个字节的数据到目标函数指向的内存里

  这个函数在遇到'\0'的时候不会停下来

  如果源函数和目标函数有任何的重叠,那么复制的结果的都是未定义的

函数的模拟实现:

#include<stdio.h>
#include<string.h>
//模拟实现memcpy
void* my_memcpy(void* dest, const void* src, int num)
{
	void* a = dest;
	while (num--)
	{
		*(char*)dest = *(char*)src;
		((char*)dest)++;
		((char*)src)++;
	}
	return a;
}
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	memcpy(arr2, arr1, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}
	printf("\n");
	int arr3[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr4[10] = { 0 };
    my_memcpy(arr4, arr3, 20);
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr4[i]);
	}
	return 0;
}

然而这个函数是有缺陷的,当拷贝的有重叠时,会存在问题:

例如:我把arr中的1 2 3 4 5拷贝到 4 5 6 7 8 上是会有问题,理论上是1 2 3 1 2 3 4 5 9 10

其实是这个:

那么当我们需要这样的复制时,要怎么办呢?这是我们就需要另一个内存函数了——memmove

  2.  memmove——内存拷贝函数

函数模型:

函数的作用: 

  和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的
  如果源空间和目标空间出现重叠,就得使用memmove函数处理

分析情况:

当把1->4的时候2->5的时候4和5就发生了改变,后面再把4->7,5->8的时候4和5就不是原来的4和5了。那么怎么改变之个问题呢?当我们从后往前走时就不会发生这种情况了,就是先把4->7,5->8之后再把1->4,2->5,这样就不会发生问题了。但是我们只用后往前走就可以解绝所有问题了吗?答案当然是不可以的。我们举个例子来看看。

这种情况从后往前走就会把4,5改变,就是8->5,7->4之后再把5->2,4->1是4和5就不是原来的值了,所以这时就需要从前往后走。那么我们什么时候从前往后什么时候从后往前呢?让我们来解决这个问题。

函数的模拟实现: 

#include<stdio.h>
#include<string.h>
#include<assert.h>
//模拟实现memmove
void* my_memmove(void* dest, const void* src, int num)
{
	void* a = dest;
	assert(dest && src);
	if (dest < src)
	{
		//src从前往后
		while (num--)
		{
			*(char*)dest = *(char*)src;
			((char*)dest)++;
			((char*)src)++;
		}
	}
	else
	{
		//src从后往前
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}
}
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	memmove(arr + 2, arr, 5 * sizeof(int));
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz1 = sizeof(arr1) / sizeof(arr1[0]);
	my_memmove(arr1 + 2, arr1, 5 * sizeof(int));
	for (i = 0; i < sz1; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

3.  memset——填充内存的函数

函数的模型: 

作用和注意事项:

作用:memset是⽤来设置内存的

注意:将内存中的值以字节为单位设置成想要的内容

函数的实现:

 

4.  memcmp——比较两个内存的函数

函数模型: 

函数作用: 

   比较从ptr1和ptr2指针指向的位置开始,向后的num个字节

  两个内存中不匹配的第一个字节在ptr1>ptr2时候,返回大于0的数

  两个内存中不匹配的第一个字节在ptr1<ptr2时候,返回小于0的数,相当返回0

总结:

     本次是从函数的介绍来初步了解字符串函数和内存函数,再通过模拟库函数的实现来深刻理解函数的使用。

这次我们的文章就到这里结束了,当然这篇文章还有很多不足之处,希望大家可以指正。

OK  让我们下个文章再见吧!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值