13

コンソールとログ ファイルにログを記録しようとする機能がありますが、機能しません。可変長引数を 2 回目に使用すると、コンソールにガベージが書き込まれます。何か案は?

    void logPrintf(const char *fmt, ...) {
        va_list ap;    // log to logfile
        va_start(ap, fmt);
        logOpen;
        vfprintf(flog, fmt, ap);
        logClose;
        va_end(ap);
        va_list ap2;   // log to console
        va_start(ap2, fmt);
        printf(fmt, ap2);
        va_end(ap2);
    }
4

3 に答える 3

14

printf()元のコードは、使用する必要がある場所で使用しようとするため、失敗しますvprintf()logOpenandステートメントのような疑わしい点logCloseを額面通りに取ると (表記を考えると、おそらくそれらはflogファイル ストリームを開いたり閉じたりするマクロです)、コードは次のようになります。

void logPrintf(const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    logOpen;
    vfprintf(flog, fmt, ap);
    logClose;
    va_end(ap);
    va_list ap2;
    va_start(ap2, fmt);
    vprintf(fmt, ap2);
    va_end(ap2);
}

2 つの個別のva_list変数を使用する必要は特にありません。もう一度使用する前に、同じものを2回使用しても問題ありませんva_end()va_start()

void logPrintf(const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    logOpen;
    vfprintf(flog, fmt, ap);
    logClose;
    va_end(ap);
    va_start(ap, fmt);
    vprintf(fmt, ap);
    va_end(ap);
}

va_list値が別の関数 (vfprintf()およびこのコード) に渡されるvprintf()と、現在の関数では使用できなくなったと想定する必要があります。それを呼び出すだけで安全va_end()です。

va_copy()このコードでは は必要ありません。動作しますが、必要ありません。関数が渡され、リストを2回処理する必要がva_copy()ある場合など、他の状況で必要になります。va_list

void logVprintf(const char *fmt, va_list args1)
{
    va_list args2;
    va_copy(args2, args1);
    logOpen;
    vfprintf(flog, fmt, args1);
    logClose;
    vprintf(fmt, args2);
    va_end(args2);
}

このコードでは、va_end()onを呼び出すのは呼び出し元のコードの責任であることに注意してくださいargs1。実際、標準は次のように述べています。

va_startおよびマクロの各呼び出しは、同じ関数内va_copyのマクロの対応する呼び出しと一致する必要があります。va_end

logVprintf()関数はva_startorのどちらも呼び出さないため、va_copyinitializeargs1を正当va_endに呼び出すことはできませんargs1。一方、標準では、 を呼び出す必要がありva_endますargs2

関数は今logPrintf()の観点から実装できます。logVprintf()

void logPrintf(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    logVprintf(fmt, args);
    va_end(args);
}

この構造 — a を受け取る操作関数va_listと、省略記号 (可変引数) を取り、それらを a への変換後に操作関数に渡すカバー関数va_list— は、多くの場合、うまく機能します。遅かれ早かれ、通常はva_list引数付きのバージョンが必要になります。

于 2016-12-31T21:01:37.693 に答える
1

C++ に似たコンパイラをアップグレードします。

template <typename... Args>
void logPrintf(const char *fmt, Args&&... args) {
    logOpen;
    fprintf(flog, fmt, args...);
    logClose;

    printf(fmt, args...);
}

もちろん、型安全なバージョンのprintfandを提供するのは良い趣味でしょうfprintf

于 2012-02-16T11:17:17.767 に答える
-2

私はこの方法がより理にかなっていると思います:

void logPrintf(const char *fmt, ...) {
        va_list ap;    // log to logfile
        va_start(ap, fmt);
        logOpen;
        vfprintf(flog, fmt, ap); //logfile
         printf(fmt, ap); //console
        logClose;
        va_end(ap);
    }
于 2012-02-16T10:11:38.883 に答える