1

我又有一个关于 C 的工作原理的问题。(由 VS2012 编译的 ANSI-C)

我正在将独立程序 (.exe) 重构为 .dll。到目前为止,这工作得很好,但是在记录日志时我偶然发现了一些问题。让我解释:

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

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

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

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

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

我有两种可能:

  1. char z88rlog[REALLY_HUGE];
  2. 动态分配内存

在我看来,第一种方法是被忽略的,因为:

  • 内存的潜在浪费是相当巨大的
  • 我仍然可能需要比 更多的内存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. 如果不是,我分配内存的大小为我的日志条目(+1 为 my \0)并将内容复制tmpstrz88rlog. 如果是的话,我会重新分配内存的大小+ (+1)z88rlog的长度。tmpstr然后使用 . 将两个“字符串”连接起来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 sizehas的末尾放置了一个断点 - 根据调试器 - 1041的值。

因此,我有 2 种(或 3 种)方法想要实现这种“字符串增长”,但没有一种有效。给我大小的错误可能会让我得出数组已经变大的结论吗?我希望我能很好地解释我想做什么,有人可以帮助我:-)提前谢谢!

对Maybee具有讽刺意味的是,我应该去为计算机购买一些新堆。它适合 RAM 插槽吗?谁能推荐一个好品牌?讽刺

4

3 回答 3

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 服务器中执行,它几乎可以肯定是在多线程环境中运行。由于您有一个全局变量,因此需要同步访问。

于 2013-04-03T11:30:40.113 回答
1

你似乎有很多问题。首先,在您的realloc通话中,您不会为终止'\0'字符分配空间。在您的第二个解决方案中,您的解决方案strlen(tmpstr+1)不正确。在您的第二个解决方案中,您还使用strcat附加到现有缓冲区z88rlog,如果它不够大,您将覆盖未分配的内存,或者覆盖分配给其他东西的数据。的第一个参数strcat是目标,这也是函数返回的内容,因此您也释放了新分配的内存。

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

于 2013-04-03T11:30:13.373 回答
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
于 2013-04-03T11:42:43.633 回答