C:动态char-array崩溃堆

时间:2013-04-03 11:27:06

标签: c char malloc heap realloc

我还有一个关于C的工作问题的问题(VS2012编译的ANSI-C)

我正在将一个独立程序(.exe)重构为.dll。这种方法到目前为止工作正常,但在记录时遇到了各种问题。让我解释一下:

原始程序 - 在运行时 - 将一个日志文件和打印信息写入屏幕。由于我的dll将在网络服务器上运行,许多人同时访问

  • 没有真正机会正确处理日志文件(并在其后清理)
  • 没有人会看到的控制台窗口

所以我的目标是将所有放在日志文件或屏幕上的内容写成类似字符串的变量(我知道C中没有字符串),然后我可以将其传递给调用者(也是一个dll,但用C#编写)。

因为在C中这样的事情是不可能的:

char z88rlog;
z88rlog="First log-entry\n";
z88rlog+="Second log-entry\n";

我有两种可能性:

  1. char z88rlog[REALLY_HUGE];
  2. 动态分配内存
  3. 在我看来,第一种方式是被忽略,因为:

    • 记忆的潜在浪费是相当巨大的
    • 我仍然需要比REALLY_HUGE更多的内存,因此会产生缓冲区溢出

    这让我有了第二种方式。我已经做了一些工作,并提出了两个解决方案,其中任何一个都无法正常工作。

    /* Solution 1 */    
    void logpr(char* tmpstr)
    {
        extern char *z88rlog;
        if (z88rlog==NULL)
        {
            z88rlog=malloc(strlen(tmpstr)+1);
            strcpy(z88rlog,tmpstr);
        }
        else
        {
            z88rlog=realloc(z88rlog,strlen(z88rlog)+strlen(tmpstr));
            z88rlog=strcat(z88rlog,tmpstr);
        }
    }
    

    在解决方案1中(等于解决方案2,你会发现)我通过char tmpstr[255];传递新的日志条目。我的“日志文件”z88rlog是全局声明的,因此我需要extern才能访问它。然后我检查是否已为z88rlog分配了内存。如果不是,我将内存大小分配给我的日志条目(我的\0为+1),并将tmpstr的内容复制到z88rlog。如果是,我将z88rlog的内存重新分配给它的大小+ tmpstr(+1)的长度。然后使用strcat连接两个“字符串”。使用断点,直接窗口我获得了以下输出:

    z88rlog
    0x00000000 <Schlechtes Ptr>
    z88rlog
    0x0059ef80 "start Z88R version 14OS"
    z88rlog
    0x0059ef80 "start Z88R version 14OS
    opening file Z88.DYNÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍýýýý««««««««þîþîþîþ"
    

    它显示了三次连续的logpr调用(strcpy / strcat之前的断点)。最后无法辨别的胡言乱语是由内存分配造成的。之后,VS发出一条错误消息,告知调试器在realloc.c中设置断点。因为这显然不起作用我编造了我的精彩解决方案2:

    /* Solution 2 */
    void logpr(char* tmpstr)
    {
        extern char *z88rlog;
        char *z88rlogtmp;
        if (z88rlog==NULL)
        {
            z88rlog=malloc(strlen(tmpstr)+1);
            strcpy(z88rlog,tmpstr);
        }
        else
        {
            z88rlogtmp=malloc(strlen(z88rlog)+strlen(tmpstr+1));
            z88rlogtmp=strcat(z88rlog,tmpstr);
            free(z88rlog);
            z88rlog=malloc(strlen(z88rlogtmp)+1);
            memcpy(z88rlog,z88rlogtmp,strlen(z88rlogtmp)+1);
            free(z88rlogtmp);
        }
    }
    

    这里我的目标是创建我的日志文件的副本,释放原始内存为新大小的原始内容创建新内存并将内容复制回来。并且不要忘记释放临时副本,因为它是通过malloc分配的。当它到达free时会立即崩溃,再次告诉我堆可能已经坏了。

    所以暂时免费评论。这确实更好 - 令我感到宽慰 - 但是在构建日志字符串时突然不会复制z88rlogtmp中的所有字符。但是一切仍然有效。直到我突然再次被告知堆可能被破坏并且调试器在_heap_alloc (size_t size) in malloc.c size的末尾放置一个断点 - 根据调试器 - 值1041。 / p>

    所以我有2(或3)种方法可以实现这种“字符串增长”,但没有一种方法可行。可能错误给我的大小指向我的结论,阵列变得很大?我希望我能很好地解释我想做什么,有人可以帮助我:-)提前谢谢!

    讽刺也许我应该去为计算机购买一些新堆。它适合RAM插槽吗?任何人都可以推荐一个好品牌吗? 讽刺

3 个答案:

答案 0 :(得分:2)

这是解决方案1 ​​中的一个错误:

z88rlog=realloc(z88rlog,strlen(z88rlog)+strlen(tmpstr));

因为没有为终止空字符分配空间。请注意,必须将realloc()的结果存储到临时变量中,以避免在发生故障时发生内存泄漏。纠正:

char* tmp = realloc(z88rlog, strlen(z88rlog) + strlen(tmpstr) + 1);
if (tmp)
{
    z88rlog = tmp;
    /* ... */
}

解决方案2 中的错误:

z88rlogtmp=malloc(strlen(z88rlog)+strlen(tmpstr+1));
                                      /*^^^^^^^^^*/

它比tmpstr的长度小一个。纠正:

z88rlogtmp=malloc(strlen(z88rlog) + strlen(tmpstr) + 1);

指针重新分配导致未定义的行为:

    z88rlogtmp=strcat(z88rlog,tmpstr);
    /* Now, 'z88rlogtmp' and 'z88rlog' point to the same memory. */

    free(z88rlog);
    /* 'z88rlogtmp' now points to deallocated memory. */

    z88rlog=malloc(strlen(z88rlogtmp)+1);
    /* This call   ^^^^^^^^^^^^^^^^^^ is undefined behaviour,
       and from this point on anything can happen. */

    memcpy(z88rlog,z88rlogtmp,strlen(z88rlogtmp)+1);
    free(z88rlogtmp);

此外,如果代码在Web服务器中执行,则几乎可以肯定在多线程环境中运行。由于您有全局变量,因此需要同步访问。

答案 1 :(得分:1)

你似乎有很多问题。要在realloc调用中开始,请不要为终止'\0'字符分配空间。在您的第二个解决方案中,您strlen(tmpstr+1)不正确。在第二个解决方案中,您还使用strcat附加到现有缓冲区z88rlog,如果它不够大,则会覆盖未分配的内存或覆盖其他内容的数据。 strcat的第一个参数是目标,也就是函数返回的参数,所以你也松开了新分配的内存。

第一个解决方案,realloc,如果你只记得分配那个额外的字符,应该可以正常工作。

答案 2 :(得分:1)

在解决方案1中,您需要为终止NULL字符分配空间。因此,realloc应该包括一个空格,即

z88rlog=realloc(z88rlog,strlen(z88rlog)+strlen(tmpstr) + 1);

在第二个解决方案中,我不确定这个z88rlogtmp=strcat(z88rlog,tmpstr);,因为z88rlog是目标字符串。如果您只想执行malloc,那么

 z88rlogtmp=malloc(strlen(z88rlog)+1 // Allocate a temporary string
 strcpy(z88rlogtmp,z88rlog); // Make a copy
 free(z88rlog); // Free current string
 z88rlog=malloc(strlen(z88rlogtmp)+ strlen(tmpstr) + 1)); //Re-allocate memory
 strcpy(z88rlog, z88rlogtmp); // Copy first string
 strcat(z88rlog, tmpStr);  // Concatenate the next string
 free(z88rlogtmp); // Free the Temporary string