为什么这段代码会破坏内存?

时间:2009-03-05 13:54:22

标签: c++ memory-corruption

这是一个相当新手的问题,应该能够合理地迅速回答......

基本上,在 echo 中第一次调用 Printf 之后, args 的内容已损坏。听起来像我正在错误地传递指针。但无法弄清楚为什么?

#define MAX_PRINT_OUTPUT 4096

void Echo(char *args[MAX_COMMAND_ARGUMENTS], int argCount)
{
    for (int i = 1; i < argCount; ++i)
    {
        Printf("%s ", args[i]);
        Printf("\n");
    }
};

void Printf(const char *output, ...)
{
    va_list args;
    char formattedOutput[MAX_PRINT_OUTPUT];

    va_start(args, output);
    vsnprintf(formattedOutput, sizeof(formattedOutput), output, args);
    va_end(args);

    g_PlatformDevice->Print(formattedOutput);
};

void RiseWindows::Print(const char *output)
{
    //Corruption appears to occur as soon as this function is entered
    #define CONSOLE_OUTPUT_SIZE 32767

    char buffer[CONSOLE_OUTPUT_SIZE];
    char *pBuffer = buffer;
    const char *pOutput = output;
    int i = 0;

    while (pOutput[i] && ((pBuffer - buffer) < sizeof(buffer) - 1))
    {
        if (pOutput[i] == '\n' && pOutput[i+1] == '\r' )
        {
            pBuffer[0] = '\r';
            pBuffer[1] = '\n';
            pBuffer += 2;
            ++i;
        }
        else if (pOutput[i] == '\r')
        {
            pBuffer[0] = '\r';
            pBuffer[1] = '\n';
            pBuffer += 2;
        }
        else if (pOutput[i] == '\n')
        {
            pBuffer[0] = '\r';
            pBuffer[1] = '\n';
            pBuffer += 2;
        }
        else
        {
            *pBuffer = pOutput[i];
            ++pBuffer;
        }
        ++i;
    }
    *pBuffer = 0;

    SendMessage(this->ConsoleWindow.hwndBuffer, EM_LINESCROLL, 0, 0xffff);
    SendMessage(this->ConsoleWindow.hwndBuffer, EM_SCROLLCARET, 0, 0);
    SendMessage(this->ConsoleWindow.hwndBuffer, EM_REPLACESEL, 0, (LPARAM)buffer);

};

注意这不是生产代码,只是概念验证 编辑 g_PlatformDevice的类型为RiseWindows,如果不清楚的话......
编辑这是在vs2008

下运行的Windows XP平台上

更新 对于任何感兴趣的人来说,问题似乎是一个溢出的调用堆栈,在堆栈的下方,然后定义了另一个大型数组。重构这可以消除内存损坏。所以用粉笔来筹码!

10 个答案:

答案 0 :(得分:3)

您尚未提及此代码运行的环境。可能是你在吹嘘。您在RiseWindows :: Print中声明堆栈上的32767字节数组。在我熟悉的某些嵌入式系统环境中,这将是个坏消息。你可以增加堆栈大小和/或在堆上分配缓冲区来测试该理论吗?您可能希望将该缓冲区改为std :: vector,或者可能是私有成员向量,以避免每次调用Print时分配和重新分配它。

沿着这些方向,MAX_PRINT_OUTPUT有多大?

答案 1 :(得分:2)

可能不是你要问的bug,但是在你的循环中,你在某些情况下是双重递增pBuffer,这可能会推动你超过缓冲区的末尾,因为你只检查长度为1(对于空终止)。

答案 2 :(得分:2)

你有没有尝试过分而治之的策略?

  • 开始注释掉行直到它起作用。
  • 一旦它正常工作,取消注释直到你碰到错误的位置。

在单独的窗口中观察args []指向的内存,而您一步一步也可以提供帮助。

答案 3 :(得分:2)

随机猜测:我觉得问题是由Printf中的这一行引起的:

char formattedOutput[MAX_PRINT_OUTPUT];

我认为这是因为你有一些明显声明的指针和一些明显未申报的指针。一个字符数组是一个指针 - 没有办法解决这个问题,但这并不明显。在Echo的函数定义中,args被定义为TWO DIMENSIONAL ARRAY,因为你将它作为

*args[MAX_COMMAND_ARGS]
你想要吗?我的猜测是无意中将某些内容作为引用而不是值传递,因为什么是指针与数组的模糊定义,并且您将指针传递给指针,该指针是数组的开头,而不是指针。数组的开头。因为当你进入RiseWindows :: Print时你说它被破坏了我的猜测是你传递了错误的东西。

另外,指向char的const指针只保留指针的值,据我所知,而不是指针内容的值。

答案 4 :(得分:1)

我是否可以建议逐步使用调试器来查看代码损坏的位置?

答案 5 :(得分:1)

while(pOutput [i]&amp;&amp;((pBuffer - buffer)&lt; sizeof(buffer) - 1))

更改为:

while(pOutput [i]&amp;&amp;((pBuffer - buffer)&lt; sizeof(buffer) - 2))

您一次只能写2个字符,因此您需要确保有两个字符的空间。

答案 6 :(得分:0)

不确定它是否应该如何工作但是Echo没有打印出args的第一个元素

// Changed i=1 to i=0;
for (int i = 0; i < argCount; ++i)
{
    Printf("%s ", args[i]);
    Printf("\n");
}

答案 7 :(得分:0)

您希望将#define从函数调用中移出到文件顶部:

Preprocessor directives can appear anywhere in a source file, but they apply only to the remainder of the source file.

在这种情况下,这可能不会导致腐败,但这是非标准的,很容易引起问题。

答案 8 :(得分:0)

工作理论是我们用以下方式吹嘘:

char buffer [CONSOLE_OUTPUT_SIZE]; char * pBuffer = buffer;

而是尝试:

char * pBuffer = new char [CONSOLE_OUTPUT_SIZE];

然后记得最后调用delete [] pBuffer。

答案 9 :(得分:0)

我还没有真正研究过这个问题,但是你的类型已经混淆了......这非常迂腐,但它确实对C有所影响。

在这里,你有一个char数组。

char formattedOutput[MAX_PRINT_OUTPUT];

在这里,你有一个需要const char指针的函数。

void RiseWindows::Print(const char *output)

尝试:

void RiseWindows::Print(const char output[])

另外,我注意到你正在修改这些缓冲区中的内存 - 你确定可以这样做吗?至少,我确信你不能在不分配更多内存的情况下习惯性地使用更多内存。 (提示提示!)

我会分配自己的数组,并将字符串复制到其中。 然后,我将使用字符串函数替换适用的换行符。

最后,我强烈建议你在这里使用std :: string。 (虽然你无法将这些内容放入varargs中 - 你必须使用c-strings,但是当你可以的时候将它们复制回std :: string中。)