这是一篇介绍内存操作函数的博客,包含memcpy、memmove、memcmp、memset的具体介绍及模拟实现,也与相似的字符串操作函数进行了比较。
一、MSDN中的注解
1、memcpy
在缓冲区之间拷贝字符; 也就是从src中拷贝count个字节的数据到dest中。
2、memmove
将一个缓冲区移动到另一个缓冲区; 也是从src中拷贝count个字节的数据到dest中,既然两个函数的作用相同,那为什么又增加了memmove呢? 是因为 memmove可以实现重叠区域的拷贝 。
举例来说:
如下数组src指向3的地址,dest指向5的地址,此时若要将src中的5个字符复制到dest中,使用memcpy和memmove便会有明显的区别;
- 使用memcpy时: 由于字符复制是从前往后进行的,复制3时,将5改成了3,复制4时,将6改成了4,如此便会造成原本的5和6丢失,最终复制的结果便是:
- 使用memmove时 而memmove会根据src与dest的地址大小进行判断,从而选择不同的复制方法,在此例中,memmove便会使用从后往前复制的方法,最终可以正确完成复制:
3、memcmp
比较两个缓冲区中的字符; 与strcmp相似,返回值为int,buf1>buf2返回正值,buf1<buf2返回负值,相等返回0;增加的count则是需要比较的字节数目。
4、memset
将缓冲区设置为一个特定的字符;其实就是初始化,特殊的是:memset会将从dest开始后count个字节全部设置为c值。
二、模拟实现
1、memcpy
- 将void类型的至今强制转换为char*指针,进行复制操作;
- 随后以char*确定步长为1,逐字节复制;
- 用size_t的无符号整型n接收需要复制的长度,自减完成复制操作;
- assert断言,确保dest与src不为空指针;
如下:
void* my_memcpy(void* dest, const void* src, size_t n)
{
assert(dest && src);
void* ret = dest;
//从前往后复制
while (n--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
2、memmove
增加对dest与src的地址大小判断,由于计算机保存数据从低地址开始,所以:
- src < dest时,从前往后复制;
- 反之,从后往前复制;
- 在进行从后往前的复制时,只需将void的指针强制转换为char,随后加上需要复制的字节数n,便可到达需要复制的内存末尾;
如此便可实现重叠字符串的复制效果:
void* my_memmove(void* dest, const void* src, size_t n)
{
//断言确保不为空指针
assert(dest && src);
void* ret = dest;
//从前往后复制
if (dest < src)
{
while (n--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
//从后往前复制
else
{
while (n--)
{
*((char*)dest + n) = *((char*)src + n);
}
}
return ret;
}
3、memcpy
memcpy的模拟实现与strcpy的有很大相似之处,不同点便是:
- memcmp利用void*类型接收,因此在逐字节比较时需要强制类型转换,同样在往后走时,也需要强制类型转换为char*,从而确定步长;
- 二者的循环结束调节不同,strcpy以‘\0’作为判别标志,而memcpy以确定的大小传参,比较完成便结束,若不同则返回结果,以此带来的,memcpy也更为安全;
int my_memcmp(const void* e1, const void* e2, size_t n)
{
assert(e1 && e2);
while (n--)
{
if (*(char*)e1 != *(char*)e2)
{
return *(char*)e1 - *(char*)e2;
}
e1 = (char*)e1 + 1;
e2 = (char*)e2 + 1;
}
return 0;
}
4、memset
此函数相对简单,只需将void*指针强制类型转换后赋值即可;
void* my_memset(void* dest, int c, size_t n)
{
assert(dest);
void* ret = dest;
while (n--)
{
*(char*)dest = c;
dest = (char*)dest + 1;
}
return ret;
}
三、易错点
- 内存操作函数是以字节为单位进行函数运算,所以,当一段代码在大端和小端模式上分别运行时,结果可能不同。