问题描述
问题应该很简单,但我花了几个小时来解决这个问题,但看不出我的逻辑有什么问题。输出正常工作,但 Valgrind 打印应该修复的内存问题。我已将 origdest = (char*)realloc(origdest,strlen(origdest) + i * sizeof(char));
代码添加到 while 循环中,我的问题是为什么不动态调整内存? Valgrind 给出的准确错误是
==9== Invalid write of size 1
==9== at 0x1087E2: mystrcat (mystrcat.c:18)
==9== by 0x10883C: main (mystrcat.c:34)
==9== Address 0x522d046 is 6 bytes inside a block of size 7 free'd
==9== at 0x4C31D2F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9== by 0x1087C2: mystrcat (mystrcat.c:17)
==9== by 0x10883C: main (mystrcat.c:34)
==9== Block was alloc'd at
==9== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9== by 0x108811: main (mystrcat.c:31)
char *mystrcat(char *dest,const char *src)
{
char *origdest = dest;
while(*dest) {
dest++;
}
int i = 1;
while (*src) {
origdest = (char*)realloc(origdest,strlen(origdest) + i * sizeof(char));
*dest++ = *src++; // copies character and increases/moves pointer
i++;
}
*dest = 0;
return origdest;
}
int main(void)
{
char *str = malloc(7);
strcpy(str,"Mydogs");
str = mystrcat(str,"arecool");
printf("%s\n",str);
free(str);
}
解决方法
本声明:
Address 0x522d046 is 6 bytes inside a block of size 7 free'd
表示在这些语句之后调用的 realloc() 导致旧指针指向已释放的内存。
在此段之后:
char *origdest = dest;
while(*dest) {
dest++;
}
编辑以解决评论“代码有什么特别的问题以及可以更改哪些内容以使其正常工作?”
我在上面的第一个观察结果的解释是,一旦指向已分配内存的指针移动,就像您所做的那样,内存分配表不再具有该内存的准确位置,从而使该内存不可释放。
您在此处声明的目标是创建 strcat()
的版本,因此使用 realloc()
是一种合理的方法,但要安全地使用它,请先将新内存分配到临时缓冲区中,然后如果分配失败,原来的内存位置还存在,可以释放。
另一个产生重大影响的小变化是 i
的初始化方式。如果使用 1,它会将第二个字符串的开头在内存中多放一个位置,在第一个字符串之后留下一个 \0
字符,从而有效地使其成为结果字符串的结尾。即您永远不会看到字符串的附加部分:
在内存中它看起来像这样:
|M|y|d|o|g|s|\0|a|r|e|c|o|o|l|
然后在尝试在串联缓冲区的 和 处放置另一个 NULL 终止符时溢出缓冲区,从而导致未定义的行为。
您的代码的以下改编说明了这些以及其他一些简化:
char *mystrcat(char *dest,const char *src)
{
char *temp = NULL;
int i = 0;//changed from i = 1 as first location to
//copy to is len1,not len1 + 1
//Note,starting at len1 + 1 would leave a NULL character
//after "Mydogs",effectively ending the string
//the following are simplifications for use in realloc()
int len1 = strlen(dest);
int len2 = strlen(src);
//call once rather than in a loop. It is more efficient.
temp = realloc(dest,len1+len2+1);//do not cast return of realloc
if(!temp)
{
//handle error
return NULL;
}
dest = temp;
while(*src)
{
dest[len1 + i] = *src;
i++;
src++;
}
dest[len1 + i] = 0;//add null terminator
return dest;
}
int main(void)
{
char *temp = NULL;
char *str = malloc(7);
if(str)//always a good idea to test pointer before using
{
strcpy(str,"Mydogs");
temp = mystrcat(str,"arecool");
if(!temp)
{
free(str);
printf("memory allocation error,leaving early");
return 0;
}
str = temp;
printf("%s\n",str);
free(str);
}
return 0;
}
为什么是 not correct to cast the return of c-m-realloc() in C。
,这里移动到原始字符串的末尾:
while(*dest)
dest++;
这里分配了一些新的内存,但 dest
仍然指向原始字符串的末尾。所以你在原始字符串结束后覆盖内存。由于您正在重新分配,原始字符串甚至可能不再存在于您之前写入的位置,因为 realloc 可以将数据移动到一个全新的位置。
while (*src)
{
origdest = (char*)realloc(origdest,strlen(origdest) + i * sizeof(char));
*dest++ = *src++; // Copies character and increases/moves pointer
i++;
}