目录:
memcpy函数
函数memcpy定义:
简单的使用方法介绍:
memcpy的模拟实现
memmove函数
函数memeove定义:
简单的使用方法介绍:
memcpy的模拟实现
画图分析:
代码实现
关于memcp和memmove该如何选择
memcmp函数
函数memcmp的定义:
简单的使用方法介绍:
memset函数
函数memset的定义:
简单的使用方法介绍:
错误用法:
memcpy函数
使用时要使用<string.h>头文件
函数memcpy定义:
void * memcpy ( void * destination, const void * source, size_t num );
简单的使用方法介绍:
函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
这个函数在遇到 '\0' 的时候并不会停下来
如果source和destination有任何的重叠,复制的结果都是未定义的。
![](https://i-blog.csdnimg.cn/blog_migrate/c702f0340d1940a830fb49a045835243.png)
memcpy的模拟实现
void* my_memcpy(void* dest, const void* src, size_t num)
{
*(char*)dest = *(char*)src;
//(*(char*)dest)++;
//(*(char*)src)++;
dest=(char*)dest+1;
src= (char*)src+1;
}
因为传的是字节,所以这里强制转换为char类型,一个一个字节来拷贝;强制转换是临时性的,因此若想再往后移一个字节,需要重新强制转换,而4,5行代码不能保证任何情况下都是对的,因此我们可以使用6,7行代码的形式。
void* my_memcpy(void* dest, const void* src, size_t num)
{
assert(dest && src);
while(num--)
{
*(char*)dest = *(char*)src;
//(*(char*)dest)++;
//(*(char*)src)++;
dest=(char*)dest+1;
src= (char*)src+1;
}
}
这里我们可以用while循环拿num来判断循环是否停止,并且可以用assert来断言一下,若是传来的有空指针,程序便不会执行下去,会使程序更加安全,assert的头文件是<assert.h>。
程序执行情况:
![](https://i-blog.csdnimg.cn/blog_migrate/3ad1235d9cbd7ba1c2b930de08915586.png)
int main()
{
int arr1[]={1,2,3,4,5,6,7,8,9,10};
my_memcpy(arr1+2,arr1,20);
for (int i = 0;i < 10;i++)
{
printf("%d ", arr1[i]);
}
retunrn 0;
}
上面的代码的理想情况应该是:1 2 1 2 3 4 5 8 9 10
那么实际输出又是什么呢?
![](https://i-blog.csdnimg.cn/blog_migrate/2a2d50f9216c6a13481f582c0f7137fe.png)
我们可以看到,和我们预期的结过并不一样,其实是因为arr1一直在被改变,发生了内存重叠(直接使用memove函数可以解决这个问题,后面我们会介绍这个函数)。我们的理想情况是3,4,5,6,7被替换为1,2,3,4,5,但是在替换过程中3,4被替换为1,2,而5,6原本是应该替换为3,4,但3,4被替换为了1,2,7应该替换为5,但5已经被替换为了1。
memmove函数
函数memeove定义:
void * memmove ( void * destination, const void * source, size_t num );
简单的使用方法介绍:
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
如果源空间和目标空间出现重叠,就得使用memmove函数处理。
![](https://i-blog.csdnimg.cn/blog_migrate/d7197f45be0f485bd4974c0d5ba2c9dd.png)
memcpy的模拟实现
画图分析:
![](https://i-blog.csdnimg.cn/blog_migrate/31f9687164ac97dd521f003be402d8c3.png)
我们不难发现,当dest(目标)的首元素在src(源)的首元素的右边时,我们将src的最后一个元素拷贝给dest的最后一个元素,这样子从后往前拷贝,就不会覆盖要拷贝的内容了
当dest(目标)的首元素在src(源)的首元素的左边时,我们将src的第一个元素拷贝给dest的第一个元素,这样子从前往后拷贝,就不会覆盖要拷贝的内容了
![](https://i-blog.csdnimg.cn/blog_migrate/67ca57126a7f9bf119a4df6ddf6225bf.png)
我们再整理一下结合上面的图便可以得出这个结论
代码实现
void* my_memmove(void* dest, const void* src, size_t num)
{
assert(dest && src);
//前-->后
if (dest < src)
{
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
//后-->前
else
{
while(num--)
{
//num为后置--,判断完后-1,例如传的是20,那么(char*)dest + num刚好指向要拷贝的第20个字节
*((char*)dest + num) = *((char*)src + num);
}
}
}
dest<src
![](https://i-blog.csdnimg.cn/blog_migrate/800c0cebe36179b89fb9816a8482ce88.png)
dest>src
![](https://i-blog.csdnimg.cn/blog_migrate/344ba286dba7af35a344092bf76e7979.png)
关于memcp和memmove该如何选择
上文中,我们自己实现的memcpy不能应对内存重叠的问题,如果你用给的现成的memcpy可以输出预期结果,但是这并不能保证永远是对的:
在C语言中:1.memcpy拷贝不重叠的内存
2. 重叠的就交给memmove
3.可以理解为使用memmove想考100分,用memcpy及格就行了
memmove>memcpy
在VS编译器中:memmove和memcpy都考了100分,但不能保证其他编译器也是如 此,在其他编译器若是也想使用memcpy拷贝重叠的内存,可以先进行测试一下。
3.那么为什么有了memove还要又memcpy,这就可能涉及到历史遗留问题,memcpy可能是先出现的,memmove后来的,但是不能随便删改库函数,因为这样子早期用到了删掉的库函数的代码就无法被新的编译器或语法所兼容
memcmp函数
函数memcmp的定义:
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
![](https://i-blog.csdnimg.cn/blog_migrate/e04c6e07d528d20f5a7209901567989e.png)
简单的使用方法介绍:
比较从ptr1和ptr2指针开始的num个字节
返回值如下:
![](https://i-blog.csdnimg.cn/blog_migrate/cf81f7e902462c1eee7f92c1334c6a9c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/0d477cf97053d4bb0c18c9d882a9c1a2.png)
memset函数
函数memset的定义:
void * memset ( void * ptr, int value, size_t num )
简单的使用方法介绍:
参数:
ptr
指向要填充的内存块的指针。
value
要设置的值。该值作为 int 传递,但该函数使用此值的无符号 char 转换填充内存块。
num
要设置为该值的字节数。
size_t 是无符号整数类型。
![](https://i-blog.csdnimg.cn/blog_migrate/5bd0bf3c4705738bb51f85568fd1411f.png)
错误用法:
![](https://i-blog.csdnimg.cn/blog_migrate/b1c1a93912ef6441036d627f9410a0d1.png)