13

我有一个尝试将内容记录到控制台和日志文件的功能,但它不起作用。可变长度参数的第二次使用会将垃圾写入控制台。有任何想法吗?

    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()logOpen从表面上看像and语句这样的可疑点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);
}

没有特别要求使用两个单独的va_list变量;在再次使用之前使用相同的两倍是完全可以的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()。它有效,但不是必需的。您需要va_copy()在其他情况下,例如当您的函数传递 ava_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(). args1事实上,标准说:

va_start和宏的每次调用va_copy都应与va_end同一函数中相应的宏调用相匹配。

由于该logVprintf()函数既不调用也不调用初始化va_start,它不能合法地调用。另一方面,标准要求它要求.va_copyargs1va_endargs1va_endargs2

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...);
}

printf当然,提供和的类型安全版本当然会很好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 回答