动态内存分配中的C编程问题
C programming problem in dynamic memory allocation
问题应该很简单,但我花了好几个小时解决这个问题,看不出我的逻辑有什么问题。输出正常工作,但 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,它会将第二个字符串的开头在内存中进一步放置一个额外的位置,在第一个字符串之后留下一个 [=17=]
字符,有效地使其成为结果字符串的结尾。即你永远不会看到字符串的附加部分:
在记忆中它看起来像这样:
|M|y|d|o|g|s|[=11=]|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++;
}
问题应该很简单,但我花了好几个小时解决这个问题,看不出我的逻辑有什么问题。输出正常工作,但 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,它会将第二个字符串的开头在内存中进一步放置一个额外的位置,在第一个字符串之后留下一个 [=17=]
字符,有效地使其成为结果字符串的结尾。即你永远不会看到字符串的附加部分:
在记忆中它看起来像这样:
|M|y|d|o|g|s|[=11=]|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++;
}