动态内存分配中的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++;
    }