C语言之内存覆盖
在实现memcpy函数的时候,我们说过要考虑内存覆盖的问题,到底什么是内存覆盖呢,他的出现对程序到底有什么影响呢?我们又要如何去解决这种问题的发生?
首先先看一般人经常实现的memcpy函数:
#include<stdio.h>
#include<assert.h>
#include<string.h>
void *my_memcpy(void *dest, const void *src, size_t count)
{
assert(dest!=NULL || src!=NULL);
char *ptmpDest = (char *)dest;
const char *ptmpSrc = (const char *)src;
while(count-- > 0)
{
*ptmpDest++ = *ptmpSrc++;
}
return dest;
}
乍一看这段代码,好像很有道理,经过一些测试也是对的,但程序到底是哪里出了问题呢?首先我们来测试一下:
void main()
{
char str[20] = "abcdefghij";
char str1[20];
my_memcpy(str1,str,strlen(str)+1);//正确拷贝
printf("str1 = %s\n",str1);
my_memcpy(str+2,str,4);//错误,拷贝出现了内存覆盖
printf("str = %s\n",str);
}
我们发现当字符串自己给自己赋值时,当目标字符串大于源字符串要拷贝的字符的个数时,程序就会出现错误,这里就涉及到了内存覆盖问题。
我们首先分析源字符串与目标字符串之间的关系:
while(count-- > 0)
{
*(ptmpDest + count) = *(ptmpSrc + count);
}
我们再来分析其他五种情况,可以得出他们的判断条件是:
ptmpSrc >= ptmpDest || ptmpDest >= ptmpSrc+count
只要满足上面的条件就不需要考虑内存重叠的问题,所以实现整体自己实现memcpy的函数代码为:
#include<stdio.h>
#include<assert.h>
#include<string.h>
void *my_memcpy(void *dest, const void *src, size_t count)
{
assert(dest!=NULL || src!=NULL);
char *ptmpDest = (char *)dest;
const char *ptmpSrc = (const char *)src;
//源地址在目的地址的右边,或者目的地址在源地址的右边,但没有交集
//直接拷贝
if(ptmpSrc >= ptmpDest || ptmpDest >= ptmpSrc+count)
{
while(count-- > 0)
{
*ptmpDest++ = *ptmpSrc++;
}
}
else //源地址在目的地址左边,并产生交集,形成内存覆盖,反着拷贝
{
while(count-- > 0)
{
*(ptmpDest + count) = *(ptmpSrc + count);
}
}
return dest;
}