资讯专栏INFORMATION COLUMN

C语言-常用内存函数详解+模拟实现

cucumber / 1430人阅读

前言:博主之前有已经写过了C语言常用字符函数详解+模拟实现,感兴趣的同学可以去围观一下哦!


目录

前言:

1.内存函数

memcpy()

 memmove()

memcmp()

memset()

2.错误信息报告函数

strerror()

 perror()


1.内存函数

memcpy()

作用:内存拷贝

函数原型:

注意:count:要拷贝的字节数

  • 函数memcpy从src位置开始向后赋值count个字节的数据到dest的内存位置,

  • 遇到 ‘/0’ 的时候不停下来

  • 如果source和destination有任何的重叠,复制的结果都是未定义的

使用样例:

int main(){	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };	int arr2[20] = { 0 };	memcpy(arr2, arr1, sizeof(int) * 10);	int i = 0;	for (i = 0;i < 20; i++)	{		printf("%d ", arr2[i]);	}	return 0;}

模拟实现:

  • 1.只知道要拷贝的字节数,不知道要拷贝的元素是什么类型。所以可以强转为char*指针->1个字节1个字节的拷贝
  • 2.返回目标空间的起始地址,注意返回类型是void*,是指针,所以可以返回 若返回类型是void,则不可以返回,
  • 3.目标空间不修改,所以可以使用const修饰
  • 4.要拷贝的字节数不可能为负数,所以接收类型可以为无符号整数
void* my_memcpy(void* dest,const void* src,size_t count ){	assert(dest&&src);	void* ret = dest;	//为了返回目标空间起始地址	//共拷贝n个字节	while(count--)	{		*(char*)dest = *(char*)src;		dest = (char*)dest + 1;//这样写是防止编译器报错		src = (char*)src + 1;	}	//若写成 dest = (char*)dest++; //有些编译器可能跑不过去	return ret;}int main(){	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };	int arr2[20] = { 0 };	my_memcpy(arr2, arr1, sizeof(int) * 10);	int i = 0;	for (i = 0;i < 10; i++)	{		printf("%d ", arr2[i]);	}	return 0;}

注意:我们这种模拟实现的方法,不能处理重叠的内存拷贝,

如:想要将数组的1234拷贝到3456中,

void* my_memcpy(void* dest, const void* src, size_t count){	assert(dest && src);	void* ret = dest;	//为了返回目标空间起始地址	//共拷贝n个字节	while (count--)	{		*(char*)dest = *(char*)src;		dest = (char*)dest + 1;//这样写是防止编译器报错		src = (char*)src + 1;	}	//若写成 dest = (char*)dest++; //有些编译器可能跑不过去	return ret;}int main(){	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };	my_memcpy(arr+2, arr, 16);	int i = 0;	for (i = 0;i < 10; i++)	{		printf("%d ", arr[i]);	}	return 0;}

 想要的效果:1212347890

使用my_memcpy得到的结果:1 2 1 2 1 2 7 8 9 10

 

但是使用库函数memcpy函数,可以实现拷贝

但是C语言只要求memcpy函数可以实现不重叠的内存拷贝即可,要实现重叠内存拷贝的话,实现memmove函数  


 memmove()

作用:内存拷贝-可处理重叠的内存拷贝

函数原型:

  • 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。

  • 如果源空间和目标空间出现重叠,就得使用memmove函数处理。


使用样例:

int main(){    int arr[] ={1,2,3,4,5,6,7,8,9,10};    memmove(arr+2,arr,16);    int i = 0;    for(i = 0;i <10;i++)    {        printf("%d ",arr[i]);    }    return 0;}

打印结果:1 2 1 2 3 4 7 8 9 10


 模拟实现:

以dest

注意:由于不知道传过来的是什么类型的数据,所以可以使用void*类型接收参数

返回目标空间的起始地址

由于只知道拷贝的字节数,不知道拷贝的类型->使用char*类型,每次拷贝一个字节

从前向后拷贝:dest和src强转为char*类型,然后+1,共拷贝n次即可


如何从后向前拷贝

->原数据的最后拷贝到目标位置的最后,然后二者不断往前拷贝

从后往前拷贝:dest和src强转为char*类型后,+count,跳过count个字节,指向要拷贝的最后一个字节,不断拷贝,count--

 

 

void* my_memmove(void* dest, void* src, size_t count){	assert(dest && src);	void* tmp = dest;//后序返回目标空间的地址	if (dest < src)	{        //共拷贝count次		while (count--)		{			//从前往后拷贝			*(char*)dest = *(char*)src;			dest = (char*)dest + 1;			src = (char*)src + 1;		}	}	else	{		//从后往前拷贝		while (count--)		{			*((char*)dest + count) = *((char*)src + count);		}	}	return tmp;}int main(){	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };	//my_memmove(arr+2,arr,16);	my_memmove(arr , arr+2, 16);	int i = 0;	for (i = 0; i < 10; i++)	{		printf("%d ", arr[i]);	}	return 0;}


memcmp()

作用:内存比较

函数原型:


 


使用strcmp()只能比较字符串,而memcmp()可以比较整形,也可以比较字符串  

使用样例:

int main(){    int arr1[] = {1,2,3,4};    //01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00    int arr2[] = {1,2,3,5};    //01 00 00 00 02 00 00 00 03 00 00 00 05 00 00 00    int ret = memcmp(arr1,arr2,12);    //比较前12个字节,二者相同,返回0    printf("%d/n",ret);//0    ret = memcmp(arr1,arr2,13);    //比较前13个字节,arr1中的04比arr2中的05小,返回-1    printf("%d/n",ret);	//-1}

 模拟实现memcmp()

注意:由于不知道传过来的是什么类型->使用void*类型接收,由于要比较的字节数恒大于0,所以用size_t接收,

由于只知道要比较的字节数,不知道是什么类型->所以使用char* 一个字节一个字节的向后比较,直到找到二者中字节对应内容不相等时,比较此时二者字节内容

int my_memcmp(const void* p1, const void* p2, size_t count){    assert(p1 && p2);    while (count--)    {        //如果二者指向的一个字节内容相等->指向下一个字节        if (*(char*) p1 == *(char*)p2)        {            p1 = (char*)p1+1;            p2 = (char*)p2+1;        }        else        {            //此时二者指向的字节内容不相同            //通过强转为char* 比较此时二者指向的一个字节内容            if ((*(char*)p1 - *(char*)p2) > 0)            {                return 1;            }            else            {                return -1;            }        }    }    //跳出循环时,count = 0,说明二者要比较的字节的内容都相同        return 0;}int main(){    int arr1[] = { 1,2,3,4 };    //01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00    int arr2[] = { 1,2,3,99 };    //01 00 00 00 02 00 00 00 03 00 00 00 05 00 00 00    int ret = my_memcmp(arr1, arr2, 12);    //比较前12个字节,二者相同,返回0    printf("%d/n", ret);    //0    ret = my_memcmp(arr1, arr2, 13);    //比较前13个字节,arr1中的04比arr2中的05小,返回-1    printf("%d/n", ret);    //-1    char* p1 = "Mangopp";    char* p2 = "Mangoppp";    ret = memcmp(p1, p2, 8);    printf("%d/n", ret);    //-1}

memset()

作用:内存设置函数

函数原型:

 

将缓冲区设置为指定的字符

字节为单位进行初始化

-----------------

使用样例:

int main(){    int arr[] = {1,2,3,4,5};        memset(arr,0,20);	//初始化20个字节内容为0        memset(arr,1,20);	//初始化20个字节内容为1    return 0;}

 

 

 


 

 注意点:以字节为单位进行初始化


 模拟实现

注意:由于不知道传过来的是什么类型->使用void*类型接收,由于要设置的字节数恒大于0,所以用size_t接收,

由于只知道要设置的字节数,不知道是什么类型->所以使用char* 一个字节一个字节的向后设置

返回目标空间的起始地址


写法1:接收类型为整形

memset:将缓冲区设置为指定的字符所以要把整形强转为字符型,再把每一个字节设置为整形对应ascii的字符

void* my_memset(void* dest, int c, size_t count){    assert(dest);    void* tmp = dest;    char set_c = (char)c;    //内存设置,共设置count个字节    while (count--)    {        *(char*)dest = set_c;        dest = (char*)dest + 1;    }    return tmp;}int main(){    int arr[] = { 1,2,3,4,5 };    my_memset(arr, 0, 20);	//初始化20个字节内容为0    my_memset(arr, 1, 20);//初始化20个字节的内容为1    return 0;}

 

写法2:接收类型为字符

若直接设置字符,要设置的是其对应的值 1 而不是其对应的ascii码值。所以要减去‘0’

‘1’ - ‘0’ -> 1 所以接收的字符还要减上字符0

void* my_memset(void* dest, char c, size_t count){    assert(dest);    void* tmp = dest;    //内存设置,共设置count个字节    while (count--)    {        *(char*)dest = c - "0";        dest = (char*)dest + 1;    }    return tmp;}int main(){    int arr[] = { 1,2,3,4,5 };    my_memset(arr, "0", 20);	//初始化20个字节内容为0    my_memset(arr, "1", 20);    return 0;}


2.错误信息报告函数


strerror()

作用:可以返回C语言内置的错误码对应的错误信息

函数原型:

返回错误码对应的错误信息

参数传错误码,翻译成错误信息,返回的是错误码对应的错误信息的字符串的首字符地址

C语言库函数调用失败的时候,会把错误码存储到errno变量中


 使用样例:

#includeint main(){     printf("%s/n",strerror(0));     printf("%s/n",strerror(1));     printf("%s/n",strerror(2));     printf("%s/n",strerror(3));    return 0;}

打印结果:
No error
Operation not permitted
No such file or directory
No such process 


 应用:

int main(){    FILE* pf = fopen("test.txt","r");    if(pf == NULL)    {        printf("%s/n",strerror(errno));	//C会把错误码放到errno变量,打印错误码对应的错误信息            }    else    {        printf("打开成功/n");    }    return 0;}//fopen():打开文件的函数,如果打开成功,返回指向该文件的文件指针FILE* 如否则返回NULL//r:只读方式打开

由于我们没有此文件:所以打印结果

No such file or directory


 perror()

作用:

打印自定义内容+strerror

函数原型:

 

string:自定义字符


使用样例:

int main(){    FILE* pf = fopen("test.txt", "r");    if (pf == NULL)    {        perror("测试");        perror("测试:/n");        perror("/n");    }    else    {        printf("打开成功/n");    }    return 0;}

 测试: No such file or directory
测试:
: No such file or directory

: No such file or directory


如果perror中有内容,先打印内容,然后自动在后面加上冒号: 然后打印错误信息

缺点:不想打印时也打印了


最后,感谢你能看到这里!如果感觉此文对你有所帮助的,欢迎留个三连呀!感谢

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/122566.html

相关文章

  • [C/C++]详解STL容器1--string的功能和模拟实现(深浅拷贝问题)

    摘要:本文介绍了类的常用接口的使用,并对其进行了模拟实现,对模拟实现中涉及到的深浅拷贝问题进行了解析。在此之前,必须提到一个经典问题。为了解决浅拷贝问题,所以中引入了深拷贝。但是实际使用中需要是第一个形参对象,才能正常使用。 本文介绍了string类的常用接口的使用,并对其进行了模拟实现,对模拟实...

    tianren124 评论0 收藏0
  • 手撕C语言进阶---字符串和内存函数(详解+实现+原码)

    摘要:自己实现时返回值可根据实际情况而定源字符串必须以结束。语言中给了一些长度受限的字符串函数,而前面的函数是长度不受限的字符串函数。拷贝个字符从源字符串到目标空间。 目录 字符函数和字符串函数 函数介绍 strlen strcpy strcat strcmp strncpy  strncat s...

    vslam 评论0 收藏0
  • [C/C++]详解STL容器3--list的功能和模拟实现(迭代器失效问题)

    摘要:本文介绍了的常用接口的使用,并对其进行了模拟实现,包括迭代器的实现。与为反向迭代器,对迭代器执行操作,迭代器向前移动。 本文介绍了list的常用接口的使用,并对其进行了模拟实现,包括list迭代器的实现。 目录 一、list的介绍 二、list的常用接口的使用 1. list的构造 2. l...

    amc 评论0 收藏0
  • 刨析C语言常用的字符串函数内存函数模拟实现

    摘要:四函数字符串追加函数介绍函数的返回值类型为,可以返回被追加的字符串的起始地址。利用函数所需的头文件函数的使用代码运行结果为函数的特点及注意事项源字符串必须以结束。目标空间必须有足够的大,能容纳下源字符串的内容。 ...

    XBaron 评论0 收藏0
  • C语言-常用字符函数详解+模拟实现

    摘要:源字符串不修改,所以用修饰找到目标空间的位置跳出循环时,指向的就是目标空间的位置,从该位置向后追加,相当于作用字符串比较函数,是比较字符串的库函数,比较的是字符串的内容,不是长度。  今天是2021年9月21日,首先祝大家中秋节快乐啦!但愿人长久,千里共婵娟。又是一年月圆,祝大家在中秋节都能...

    elliott_hu 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<