个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创C语言内存函数超详细讲解
收录于专栏【C语言学习】
本专栏旨在分享学习C语言学习的一点学习笔记,欢迎大家在评论区交流讨论💌
目录
1. memcpy使⽤和模拟实现
2. memmove使⽤和模拟实现
3. memset函数的使⽤
4. memcmp函数的使⽤
1. memcpy使⽤和模拟实现
void * memcpy ( void * destination, const void * source, size_t num );
参考:memcpy - C++ Reference (cplusplus.com)
• 函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。
• 这个函数在遇到 '\0' 的时候并不会停下来。
• 如果source和destination有任何的重叠,复制的结果都是未定义的。
对于重叠的内存,交给memmove来处理。
示例:
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]);
}
return 0;
}
输出结果:
注意:
函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。单位是字节,所以20个字节就是5个整形
memcpy函数的模拟实现:
void* my_memcpy(void* dest, void* src, size_t num)
{
void* ret = dest;
int i = 0;
assert(dest && src);
while (num--)
{
*(char*)dest = *(char*)src;
((char*)src)++;
((char*)dest)++;
}
return ret;
}
因为memcpy拷贝的数据类型是不一样的,这里我们使用void*去接收,使用char*去强转类型完成一个一个字节的拷贝
这是一个简单的
my_memcpy
函数,它的功能是将源内存块src
的内容复制到目标内存块dest
。这个函数接受三个参数:目标内存块的指针dest
,源内存块的指针src
,以及要复制的字节数num
。函数的主要步骤如下:
函数首先保存目标内存块的初始地址
dest
到ret
,以便在复制完成后返回。使用
assert
函数检查dest
和src
是否为NULL
。如果任一指针为NULL
,则assert
会终止程序。然后,函数进入一个循环,该循环将持续
num
次。在每次迭代中,它都会做以下操作:
使用
*(char*)dest = *(char*)src;
将src
指向的当前字节复制到dest
指向的当前字节。使用
((char*)src)++;
和((char*)dest)++;
将src
和dest
的地址分别增加 1,以便在下一次迭代中复制下一个字节。最后,函数返回
ret
,即目标内存块的初始地址。这个函数的实现假设
src
和dest
指向的内存区域不会重叠。如果它们重叠,那么这个函数可能会导致未定义的行为。在这种情况下,应该使用memmove
函数,而不是memcpy
。此外,这个函数也没有处理可能的内存对齐问题。在某些硬件和操作系统上,如果src
和dest
的地址不是某个特定值(如 4 或 8)的倍数,那么直接复制它们可能会导致性能下降或者错误。在这种情况下,应该使用更复杂的算法来处理对齐问题。但是,对于简单的用途,这个my_memcpy
函数应该是足够的。
测试代码:
int main()
{
//strcpy - 字符串的拷贝
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
// 0 1 2 3 4
int arr2[20] = { 0 };
//0 1 2 3 4
//memcpy - 针对内存块进行拷贝
my_memcpy(arr2, arr1, 20);
int i = 0;
for (i = 0; i < 20; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
这里我们注意一下,strcpy是对字符串的拷贝,而我们的memcpy是对内存块的拷贝
测试输出:
2. memmove使⽤和模拟实现
void * memmove ( void * destination, const void * source, size_t num );
参考:memmove - C++ 参考 (cplusplus.com)
•和memcpy的差别就是memmove函数处理的源内存块和⽬标内存块是可以重叠的。
• 如果源空间和⽬标空间出现重叠,就得使⽤memmove函数处理。
示例:
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
memmove(arr1 + 2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
输出结果:
memmove的模拟实现:
//memmove函数拷贝完成后,会返回目标空间的起始地址
void* my_memmove(void* dest, const void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
if (dest < src)
{
//前->后
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else
{
//后->前
while (num--)
{
*((char*)dest+num) = *((char*)src + num);
}
}
return ret;
}
注意:这里分为前后两种情况
分析:
这段代码是
my_memmove
函数的实现,它用于在内存中移动数据块,并且可以正确处理源和目标内存区域重叠的情况。函数接收三个参数:目标内存地址dest
,源内存地址src
,以及要移动的字节数num
。函数的工作原理如下:
- 参数检查:使用
assert
确保dest
和src
都不是空指针。- 保存返回地址:将
dest
的初始地址保存到ret
,以便函数结束时返回。- 前向拷贝:如果
dest
地址小于src
地址,说明没有重叠或者dest
在src
的前面,可以从前向后拷贝。
- 在循环中,逐字节拷贝
src
到dest
,然后将两者的地址都向后移动一位。- 后向拷贝:如果
dest
地址大于或等于src
地址,说明dest
在src
的后面,可能会有重叠,需要从后向前拷贝。
- 在循环中,从最后一个字节开始拷贝,直到拷贝完所有字节。
这样,即使
src
和dest
有重叠,数据也不会被错误地覆盖。函数最后返回ret
,即目标内存块的起始地址。这个函数是memcpy
的一个安全替代品,特别是在处理可能重叠的内存区域时。
举例:
例如,假设我们有一个数组
123456789
,并且我们想要将从3
开始的部分复制到从1
开始的位置。如果我们从前向后拷贝,那么在拷贝3
到1
的位置后,原来3
的位置就变成了1
,这样当我们想要拷贝5
到3
的位置时,就会出现问题,因为此时3
的位置已经被改变了。为了解决这个问题,
memmove
会检查dest
和src
的关系。如果dest
在src
的后面,那么memmove
就会选择从后向前拷贝。这样,即使dest
和src
有重叠,也不会出现数据被提前覆盖的问题。在上述例子中,memmove
会先拷贝最后一个元素9
,然后是8
,7
,依此类推,直到拷贝到3
。这样,即使dest
和src
有重叠,也能保证数据的正确拷贝。所以,
memmove
函数在处理可能有重叠的内存区域时,比memcpy
函数更安全。但是,如果确定dest
和src
不会重叠,那么memcpy
的效率通常会更高。因为memcpy
不需要检查dest
和src
的关系,也不需要决定是从前向后拷贝还是从后向前拷贝,所以它的实现可以更简单,运行速度也可以更快。但是,如果不能确定dest
和src
是否重叠,那么最好使用memmove
。这就是为什么标准库提供了两个不同的函数来处理内存拷贝的原因。
测试数据:
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr, arr+2, 5 * sizeof(int));
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
输出数据:
3. memset函数的使⽤
void * memset ( void * ptr, int value, size_t num );
参考: memset - C++ Reference (cplusplus.com)
memset是⽤来设置内存的,将内存中的值以字节为单位设置成想要的内容。
示例:
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "hello world";
memset(str, 'x', 6);
printf(str);
return 0;
}
调试分析:
4. memcmp函数的使⽤
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
• ⽐较从ptr1和ptr2指针指向的位置开始,向后的num个字节
• 返回值如下:
示例:
#include <stdio.h>
#include <string.h>
int main()
{
char buffer1[] = "DWgaOtP12df0";
char buffer2[] = "DWGAOTP12DF0";
int n;
n = memcmp(buffer1, buffer2, sizeof(buffer1));
if (n > 0)
printf("'%s' is greater than '%s'.\n", buffer1, buffer2);
else if (n < 0)
printf("'%s' is less than '%s'.\n", buffer1, buffer2);
else
printf("'%s' is the same as '%s'.\n", buffer1, buffer2);
return 0;
}
输出结果: