3

これはかなり初心者の質問であり、合理的に迅速に回答できるはずです...

基本的に、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 プラットフォーム上にあります。

更新 興味のある人にとっては、問題はオーバーフローした呼び出しスタックであり、スタックのさらに下にあり、この別の大きな配列が定義されていたようです。これをリファクタリングすると、メモリの破損がなくなりました。スタックバタリングにチョークアップ!

4

10 に答える 10

4

このコードが実行される環境については言及していません。スタックを吹き飛ばしている可能性があります。RiseWindows::Print のスタックで 32767 バイトの配列を宣言しています。私がよく知っている一部の組み込みシステム環境では、これは悪いニュースです。その理論をテストするためだけに、スタックサイズを増やしたり、ヒープにそのバッファを割り当てたりできますか? Print を呼び出すたびに割り当てと再割り当てを避けるために、代わりにそのバッファーを std::vector にするか、場合によってはプライベート メンバー ベクトルにすることができます。

これらの線に沿って、MAX_PRINT_OUTPUT はどのくらいの大きさですか?

于 2009-03-05T14:17:10.387 に答える
2

おそらくあなたが求めているバグではありませんが、ループでは、場合によっては pBuffer を二重にインクリメントしています。

于 2009-03-05T14:03:41.387 に答える
1

while (pOutput[i] && ((pBuffer - バッファ) < sizeof(バッファ) - 1))

への変更:

while (pOutput[i] && ((pBuffer - バッファ) < sizeof(バッファ) - 2))

一度に 2 文字を書くので、2 文字分のスペースがあることを確認する必要があります。

于 2009-03-05T14:05:06.517 に答える
1

コードが壊れている場所を確認するために、デバッガーを使用することをお勧めしますか?

于 2009-03-05T13:58:35.567 に答える
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 にコピーして戻します)。

于 2009-03-10T11:59:29.553 に答える
0

作業理論は、スタックを吹き飛ばしているということです:

文字バッファ[CONSOLE_OUTPUT_SIZE]; char *pBuffer = バッファ;

代わりに試してください:

char *pBuffer = new char[CONSOLE_OUTPUT_SIZE];

最後に delete [] pBuffer を呼び出すことを忘れないでください。

于 2009-03-09T09:51:33.143 に答える
0

それがどのように機能するかどうかはわかりませんが、Echoはargsの最初の要素を出力しません

// Changed i=1 to i=0;
for (int i = 0; i < argCount; ++i)
{
    Printf("%s ", args[i]);
    Printf("\n");
}
于 2009-03-05T14:03:58.687 に答える
0

#define を関数呼び出しからファイルの先頭に移動します。

プリプロセッサ ディレクティブは、ソース ファイルのどこにでも記述できますが、ソース ファイルの残りの部分にのみ適用されます。

これはおそらくこの場合の破損の原因ではありませんが、標準的ではないため、後で簡単に問題を引き起こす可能性があります。

于 2009-03-05T14:07:03.157 に答える