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

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


目录

前言:

1.内存函数

memcpy()

 memmove()

memcmp()

memset()

2.错误信息报告函数

strerror()

 perror()


1.内存函数

memcpy()

作用:内存拷贝

函数原型:C语言-常用内存函数详解+模拟实现_第1张图片

注意: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

C语言-常用内存函数详解+模拟实现_第2张图片 

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

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


 memmove()

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

函数原型:

C语言-常用内存函数详解+模拟实现_第3张图片

  • 和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--

C语言-常用内存函数详解+模拟实现_第4张图片

 

C语言-常用内存函数详解+模拟实现_第5张图片

 

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()

作用:内存比较

函数原型:C语言-常用内存函数详解+模拟实现_第6张图片


C语言-常用内存函数详解+模拟实现_第7张图片 


使用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()

作用:内存设置函数

函数原型:C语言-常用内存函数详解+模拟实现_第8张图片

 

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

字节为单位进行初始化

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

使用样例:

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

C语言-常用内存函数详解+模拟实现_第9张图片 

C语言-常用内存函数详解+模拟实现_第10张图片 

 


C语言-常用内存函数详解+模拟实现_第11张图片 

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


 模拟实现

注意:由于不知道传过来的是什么类型->使用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语言-常用内存函数详解+模拟实现_第12张图片

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

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

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


 使用样例:

#include
int 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

函数原型:

C语言-常用内存函数详解+模拟实现_第13张图片

 

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中有内容,先打印内容,然后自动在后面加上冒号: 然后打印错误信息

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


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

你可能感兴趣的