c语言库函数功能模拟实现

一.库函数功能以及模拟实现

1.1求字符串长度

1.1.1strlen

strlen计算的是从参数给的地址向后找,找到“\0”之前的长度,它非常在意“\0”,没有“\0”就一直疯狂向后找,即使不在它的空间内他也要找,直到找到“\0”才停止
比如一个字符串是“abcdef”,这时候没有“\0”,程序就会一直向后找,找到f的
时候,发现没有“\0”,程序就继续向后找,虽然越界了,但是只要没有“\0”就不停止,这个时候strlen计算出来的结果肯定就不对了
如果这个字符串是“abcdef\0”,计算“\0”之前的个数,就是6
当然,这里我就是举个例子,正常的,每个字符串后面都默认有“\0”,只不过是我们看不到,当你调试起来的时候就可以看到了,比如
“abc”,这时候你计算它,就是3,因为c后面默认有一个“\0”,上面我说的那个“abcdef”没有“\0”只是举例子,其实它也有,只不过我为了让大家更直观的理解
有几个注意事项:1.参数指向的字符串必须以“\0”结尾
2.参数的返回值是size_t,是无符号的(可以把size_t理解为正数),因为长度都是正数
3.它的头文件是#include<string.h>,也就是说你要用这个函数,你在程序的最开始必须加上这个
下面上代码

//这个是库函数的功能
#include<stdio.h>//这个基本你写c语言你就加上
#include<assert.h>//这个是断言的意思,就是保证你程序不会出错,如果出错,程序会给你警告,断言里面的内容必须是正确的
#include<string.h>
int main()
{
	char arr[] = "abcde";//这里我们把字符串弄成“abcde”,虽然看不到“\0”,但是它是存在的,默认在e的后面
	int sz = strlen(arr);
	printf("%d", sz);
	return 0;
}

下面我们来看库函数的模拟实现

#include<stdio.h>//这个基本你写c语言你就加上
#include<assert.h>//这个是断言的头文件
#include<string.h>
//非递归
int my_strlen(char* a)//把int换成size_t最好,但是小编习惯了用int
{
	assert(a);//这个是断言的意思,就是保证你程序不会出错,如果出错,程序会给你警告,断言里面的内容必须是正确的
	//这个断言就是保证你的指针a不会是空指针
	int count = 0;
	while (*a != '\0')
	{
		a++;
		count++;
	}
	return count;
}
int main()
{
	char arr[] = "abcde";//这里我们把字符串弄成“abcde”,虽然看不到“\0”,但是它是存在的,默认在e的后面
	int sz = my_strlen(arr);
	printf("%d", sz);
	return 0;
}
//递归
//把int换成size_t最好,但是小编习惯了用int
int my_strlen(char* str)
{
	assert(str);
	if (*str == '\0')
	{
		return 0;
	}
	return 1 + my_strlen(str+1);
}
int main()
{
	char arr[] = "abcde";
	int len = my_strlen(arr);
	printf("%d", len);
	return 0;
}
//指针-指针(两个指针之间的元素的个数)
int my_strlen(char* str)//把int换成size_t最好,但是小编习惯了用int
{
	assert(str);
	char* start = str;
	while (*str != '\0')
	{
		str++;
	}
	return str - start;
}
int main()
{
	char arr[] = "abcde";
	int len = my_strlen(arr);
	printf("%d", len);
	return 0;
}

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

1.2.1strcpy

这个的头文件是#include<string.h>
它的作用就是把一个字符串的内容拷贝到另一个字符串里
strcpy(目的地, 源头)
注意:
1.源字符串必须以“\0”结束
2.会将源字符串中的“\0”拷贝到目标空间
3.目标空间必须足够大
4.目标空间必须可变
比如下面这个就不对
因为char* p后面的是常量字符串,常量字符串不可被修改

int main()
{
	char* p = "abcdef";
	char arr2[] = "xiaobai";
	strcpy(arr1, arr2);
	printf("%s", arr1);
	return 0;
}

下面开始上代码

//strcpy的功能
int main()
{
	char arr1[20] = "xxxxxxxxx";
	char arr2[] = "xiaobai";
	strcpy(arr1, arr2);//第一个是目的地,第二个是源头,
	//这个意思就是把arr2的内容拷贝到arr1里去
	printf("%s", arr1);
	return 0;
}

strcpy的模拟实现

void my_strcpy(char* str1, char* str2)
{
	assert(str1&&str2);
	while (*str2 != '\0')
	{
		*str1 = *str2;
		str1++;
		str2++;
	}
	*str1 = *str2;//最后一步是把‘\0’也拷贝上
}
int main()
{
	char arr1[]= "xxxxxxxxxxxxx";
	char arr2[] = "xiaobai";
	my_strcpy(arr1, arr2);
	printf("%s", arr1);
	return 0;
}

1.2.2strcat

追加函数,就是把arr2的内容追加在arr1的后面
头文件是#include<string.h>
strcat(目的地,源头)
注意:
1.源字符串必须以“\0”结束
2.目标空间必须足够大,能够容纳下源字符串的内容
3.目标空间必须可修改

//功能实现
int main()
{
	char arr1[50]= "xiaobai";
	char arr2[] = "buhuixiedaima";
	strcat(arr1, arr2);
	printf("%s", arr1);
	return 0;
}
//模拟实现
void my_strcat(char* str1, char* str2)
{
	while (*str1 != '\0')//找目标空间的‘\0’
	{
		str1++;
	}
	while (*str2 != '\0')//把str2的内容从str1的‘\0’的位置开始追加
	{
		*str1 = *str2;
		str1++;
		str2++;
	}
	*str1 = *str2;//把str2的‘\0’也放到str1里面
}
int main()
{
	char arr1[50] = "xiaobai";
	char arr2[] = "buhuixiedaima";
	my_strcat(arr1, arr2);
	printf("%s", arr1);
	return 0;
}

1.2.3strcmp

这个函数是比较两个字符串大小的函数,头文件是头文件是#include<string.h>
虽然是比较大小的,但是不是比较长度的,是比较ASCII码值
比如arr1=“abcdef”;
arr2=“abz”;
strcmp(arr1,arr2)
这时候程序开始比较了,两个数组第一个都是a,一样,开始比较下一个,都是b一样,开始比较下一个,arr1的下一个是从,arr2的下一个是z,不一样了,这时候就比较c和z的
ASCII码值,z的大于c,所以arr2比arr1大
注意:strcmp(arr1,arr2)
如果arr1>arr2,函数返回大于0的数字,
arr1=arr2,函数返回0
arr1<arr2,函数返回小于0的数字
函数功能

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abz";
	int ret=strcmp(arr1, arr2);
	printf("%d", ret);
	return 0;
}

模拟实现

int my_strcmp(char* str1, char* str2)
{
	while (*str1 == *str2)
	{
		str1++;
		str2++;
	}
	if (*str1 > *str2)
	{
		return 1;
	}
	else if (*str1 == *str2)
	{
		return 0;
	}
	else
	{
		return -1;
	}
}
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abz";
	int ret=my_strcmp(arr1, arr2);
	printf("%d", ret);
	return 0;
}

strcpy,strcat,strcmp都是长度不受限制的字符串函数,什么是长度不受限制的字符串呢,就是一直到‘\0’为止,没有具体的数字概念,比如我想拷贝一个字符串的前5个数字,就是受限制的字符
不受限制的字符串函数是不安全的,因为它们不关心你放不放的下,一直执行到‘\0’为止

1.3长度受限制的字符串函数

1.3.1strncpy

跟strcpy基本一样,只不过加了一个限制长度的数字,strncpy(arr1,arr2,3)的意思就是把arr2的前三个字符拷贝到ar1里面去,注意,不拷贝‘\0’
函数功能

int main()
{
	char arr1[] = "xxxxxxxx";
	char arr2[] = "abc";
	strncpy(arr1, arr2, 3);
	printf("%s", arr1);
}

模拟实现

void my_strncpy(char* str1, char* str2,int n)
{
	int count = 0;
	while (count < n)
	{
		*(str1 + count) = *(str2 + count);
		count++;
	}
}
int main()
{
	char arr1[] = "xxxxxxxx";
	char arr2[] = "abc";
	my_strncpy(arr1, arr2, 3);
	printf("%s", arr1);
}

1.3.2strncat

也是大同小异,在目的地字符串后面追加指定的字符串的前几个字符
功能

int main()
{
	char arr1[80] = "xxxxxxxx";
	char arr2[] = "abc";
	strncat(arr1, arr2, 2);
	printf("%s", arr1);
}

模拟实现

void my_strncat(char* str1, char* str2,int n)
{
	
	while (*str1!='\0')
	{
		str1++;
	}
	int count = 0;
	while (count < n)
	{
		*(str1 + count) = *(str2 + count);
		count++;
	}
}
int main()
{
	char arr1[80] = "xxxxxxxx";
	char arr2[] = "abc";
	my_strncat(arr1, arr2, 2);
	printf("%s", arr1);
}

1.3.3strncmp

比较两个字符串的前几个字母
功能

int main()
{
	char arr1[] = "abz";
	char arr2[] = "abcdef";
	int ret=strncmp(arr1, arr2, 2);
	printf("%d", ret);
}

模拟实现

int my_strncmp(char* str1, char* str2,int n)
{
	int count = 0;
	while (count < n)
	{
		if (*str1 == *str2)
		{
			count++;
			str1++;
			str2++;

		}
		else
		{
			break;
		}
	}
	if (count == n)
	{
		return 0;
	}
	else
	{
		if (*str1 > *str2)
		{
			return 1;
		}
		if (*str1 < *str2)
		{
			return -1;
		}
	}

}
int main()
{
	char arr1[] = "abz";
	char arr2[] = "abcdef";
	int ret=my_strncmp(arr1, arr2, 3);
	printf("%d", ret);
}

1.4字符串查找

1.4.1strstr

strstr(arr1,arr2)的意思就是在arr1里面找arr2,比如
arr1是abcdeffedcba
arr2是bcde
arr1里面有arr2,就返回arr2里面第一个元素b的地址,也就是输出的时候是
bcdeffedcba,如果arr1里面有好几个arr2,就需要返回第一个arr2中第一个元素的地址
函数功能

int main()
{
	char arr1[] = "abbbbbbbcdef";
	char arr2[] = "bcdef";
	char* ret=strstr(arr1, arr2);
	printf("%s", ret);
}

模拟实现

char* my_strstr(char* str1, char* str2)
{
	if (*str2 == '\0')
	{
		return str1;
	}
	char* s1 = NULL;
	char* cp = str1;
	char* s2 = NULL;
	while (*cp != '\0')
	{
		s1 = cp;
		s2 = str2;
		while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return cp;//找到了
		}
		cp++;
	}
	return NULL;//没找到
}
int main()
{
	char arr1[] = "abbbcdbbcef";
	char arr2[] = "bbc";
	char* ret = my_strstr(arr1, arr2);

	if (ret == NULL)
	{
		printf("找不到\n");
	}
	else
	{
		printf("%s\n", ret);
	}

	return 0;
}

1.4.2strtok

char arr[] = “192#168.120.85”;
char* p = “#.”;
strtok(arr,p);
意思就是arr里面的元素都是通过p里面的元素分割的
strtok函数会改变被操作的字符串
strtok函数找到arr中的第一个标记#,并将其用‘\0’结尾,返回一个指向这个标记的指针
当找到了第一个#的时候,把它改成‘\0’之后,strtok里面一定记录了#之后的地址
如果第一次找,就是strtok(非空指针,p)
第二次往后就是strtok(空指针,p)

int main()
{
	//char arr[] = "bbbb@yaaa.net";//"@."
	char arr[] = "192#168.120.85";
	char* p = "#.";
	char buf[20] = { 0 };
	strcpy(buf, arr);
	char* ret = NULL;
	for (ret = strtok(buf, p); ret != NULL; ret=strtok(NULL, p))
	{
		printf("%s\n", ret);
	}

1.5错误信息报告

1.5.1strerror

头文件#include<string.h>
C语言的库函数在运行的时候,如果发生错误,就会将错误码放在一个变量中,这个变量是:errno
错误码是一些数字:0 1 2 3 4 5
我们需要将错误码翻译成错误信息

int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	printf("%s\n", strerror(4));
	printf("%s\n", strerror(5));//告诉你5代表什么错误信息

	return 0;
}
#include <errno.h>//errno的头文件
int main()
{
	打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		//perror的头文件是#include<stdio.h>
		perror("fopen");//可以理解成printf+strerror
		return 1;
	}
	//读文件
	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

二.内存函数功能以及模拟实现

2.1memcpy

strcpy只能针对字符串,现在我们的要求变高了,字符串不能满足我们的要求了,我们想要拷贝别的,比如数字,我们就可以用memcpy
头文件#include<string.h>

int main()
{
	int arr1[] = { 0 };
	int arr2[] = { 1,2,3,4,5,6,7,8,9 };
	memcpy(arr1, arr2 + 2, 20);//目的地,源头,字节数
	//这句话的意思是从arr2下标为2的位置开始
	//往后五个元素,都拷贝到arr1里面去,5个整形的字节数就是20
	return 0;
}

模拟实现

void* my_memcpy(void* dest, void* src, int num)//参数类型是void*,这个意思就是函数不知道要传过来的数据是什么类型,它都可以拷贝
{
	void* ret = dest;
	while (num--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;//记住最好这么写,不能写成后置++,有的编译器报错,这么写基本上都不会报错
	}
	return ret;//void*也可以返回,因为程序要把你想要的类型转换之后用到你自定义的函数里
}
int main()
{
	int arr1[] = { 0 };
	int arr2[] = { 1,2,3,4,5,6,7,8,9 };
	my_memcpy(arr1, arr2 + 2, 20);
	return 0;
}

注意,memcpy不能自己复制自己
比如memcpy(arr1,arr1+2,5)
就是错的

2.2memmove

为了解决memcpy不能复制自己的问题,就创造了memmove这个函数,它可以复制自己,其他的跟memcpy差不多

void* my_memmove(void* dest, void* src, int num)//参数类型是void*,这个意思就是函数不知道要传过来的数据是什么类型,它都可以拷贝
{
	void* ret = dest;
	while (num--)
	{
		//从前向后
		if (dest < src)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
		//从后向前
		else
		{
			while (num--)
			{
				*((char*)dest + num) = *((char*)src + num);
			}
		}
	}
	return ret;//void*也可以返回,因为程序要把你想要的类型转换之后用到你自定义的函数里
}
int main()
{
	int arr1[] = { 0 };
	int arr2[] = { 1,2,3,4,5,6,7,8,9 };
	my_memmove(arr2, arr2 + 2, 20);
	return 0;
}

2.3memcmp

比较两个数组的大小,不一定是字符串,可以是数字数组,结构体数组等

int main()
{
	int arr1[] = { 1,2,6 };//01 00 00 00 02 00 00 00 06 00 00 00
	int arr2[] = { 1,2,5 };//01 00 00 00 02 00 00 00 05 00 00 00
	int ret = memcmp(arr1, arr2, 8);//比较这两个数组的前八个字节,arr1>arr2,就是1,等于是0,小于是-1
	printf("%d\n", ret);

	return 0;
}

2.4memset(内存设置函数)

//memset - 内存设置函数
//以字节为单位来设置内存中的数据的

int main()
{
	char arr[] = "hello world";
	memset(arr, 'x', 5);//把arr数组的前五个字节变成x
	printf("%s\n", arr);
	memset(arr+6, 'y', 5);//从arr数组下标为6的元素开始,往后5个字节都变成y
	printf("%s\n", arr);
	
	//下面这两行本来是想把arr数组的元素全部变成1,但是最后结果不是我们想要的
	//这是因为memset是以字节赋值的
	int arr[10] = { 0 };
	memset(arr, 1, 40);

	return 0;
}
  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值