2013-01-16 117 views
3

我一直未能找到以下问题的答案,而且我遇到了一些与这些功能相关的问题。C++中fprintf和vfprintf有什么区别?

我的主要编程是用C#完成的,在学习时从来没有真正学过C++,但是在我目前的工作中,我还必须做一些C++编程。

大部分的C++编程都是由一名前员工完成的,他为登录做了一个功能。

偶尔此功能导致错误(访问冲突) - 这不会显示给用户,但我通过调试器运行代码时看到它。

在错误发生时它指向这行代码:

vfprintf(LogFile, fmt, va); 

然后我把在代码仔细看看之前和之后,并把上面到上下文的代码解决办法是:

void FileLog(char *fmt, ...) 
{ 
    va_list  va; 
    struct time t; 
    struct date d; 
    long   clk; 
    static int ReEntrant = 0; 

    if(FileLogEnabled == false) 
    return; 

    ReEntrant++; 
    if(ReEntrant > 1) 
    return; 

    if(LogFile == NULL) 
    LogFile = fopen(LogFileName, "a+"); 
    if(LogFile != NULL) 
    { 
    gettime(&t); 
    getdate(&d); 
    fprintf(LogFile, "\n%d-%02d-%02d %2d:%02d:%02d.%02d0> ", d.da_year, d.da_mon, d.da_day, t.ti_hour, t.ti_min, t.ti_sec, t.ti_hund); 

    va_start(va, fmt); 
    vfprintf(LogFile, fmt, va); 
    va_end(va); 

    fflush(LogFile); 
    ... 
    } 
    ReEntrant = 0; 
} 

其实我不明白为什么它需要(如果是?)同时调用fprintf和vfprintf?我会认为第一个fprintf调用会将格式化的字符串写入流(File),那就足够了?

一点解释或者一些信息,将不胜感激:)

编辑:从号注释后 - 我追查到今天经常被造成这个错误的函数的特定呼叫。

FileLog("TimerRestore[%d], Name=%s", Package.CurGame->Timers[ Index ].Name.c_str()); 

我确实认为这可能带来一些麻烦,因为“TimerRestore [%d],名称=%s”时应该遵循一个十进制和字符串arguemtn,但只有一个字符串给出参数。我需要做一些测试,但是我相信谁写这段代码的作者的意思是写:

FileLog("TimerRestore[%d], Name=%s", Index, Package.CurGame->Timers[ Index ].Name.c_str()); 

不过我还是不明白,为什么函数调用不似乎总是导致错误。或者,也许这是FileLog函数中的“ReEntrant”变量的原因,当它没有失败时,它会阻止它?

非常感谢所有的反馈和信息。

+0

请不要将C++和C绑定在一个假的术语“C/C++”中。他们是不同成语的不同语言。 –

+0

这个日志记录函数是从多个线程中调用的吗? “gettime”和“getdate”函数中是否有任何静态参数? – paddy

+1

如果您查看代码,则第一次fprintf()调用仅将日期/时间写入日志文件。 vfprintf()调用打印实际的日志消息。如果调用者为日志消息提供了自己的格式化字符串,则这些不能组合。但是,实际的错误不在调试器停止的那一行,这是因为有人用无效的参数调用了这个函数,你必须跟踪这个参数。 – nos

回答

5

​​(朋友)允许使用va_list作为参数,它是有用的,当你的函数的参数的变化量:

void log(FILE *file, const char* format, ...) 
{ 
    va_list args; 
    va_start (args, format); 
    fprintf(file, "%s: ", getTimestamp()); 
    vfprintf (file, format, args); 
    va_end (args); 
} 

在您的应用程序,你可以调用这个函数的参数的变化量:

log(file, "i=%d\n", i);   // 3 arguments 
log(file, "x=%d, y=%d\n", x, y); // 4 arguments 

我不知道为什么你的函数会导致错误。你的代码片段没有提供足够的细节。提供的函数参数的类型数量可能是是原因。

+0

是的,这也是如何在应用程序中调用日志功能:无效FileLog(char * fmt,...) – Knirkegaard

0

vfprintf让您有一个变量参数列表这意味着你可以在运行时动态创建的参数列表。

0

这在日志记录中很常见。你想制作一个printf式的日志消息,如:

Log("The value of x is now %d", x); 

但是这需要可变参数。所以你需要vfprintf。之所以使用fprintf也是因为它想写一个日期/时间戳,并且不能将这些额外的东西添加到传递给vfprintf的现有格式。

另一种方法是使用字符串版本vsprintf,并制作一个大字符串,然后将其写入文件。但更容易出错(如缓冲区溢出)。

2

首先,在C++中使用fprintf()和(特别是)vfprintf()邪恶。

要点:fprintf()是一个可变参数函数,它接受任意数量的参数。在内部,可变参数函数通过使用va_listva_start()va_end()“解包”可变参数来实现。当你想后,从自己的可变参数函数访问fprintf()功能你解开自己的可变参数

vfprintf()使用(也就是说,你可以使用一个va_list实例)。 vfprintf()不是 variadic;它接受存储参数的va_list

您尚未发布函数声明fprintf()vfprintf(),但我们可以假设它是可变的。它首先使用fprintf()将一些数据打印到LogFile中,然后使用vfprintf()在那里打印自己的可变参数。

+1

你能提供一个链接或其他证据表明它是邪恶的吗? – paddy

+2

@paddy我不知道手头没有链接,但我会解释一下自己:'fprintf()'是不安全的类型。如果你的参数和格式说明符不匹配,那么这是一个运行时错误,在iostreams中,这是一个编译时。 'vfprintf()'意味着写一个(C风格)可变参数函数,这又是一个非常不安全的类型。将不重要的类传递给这样的函数是“有条件地支持实现定义的语义”(引用标准)。 – Angew

+2

当然,但不是类型安全不会使它变得邪恶......它只是意味着你必须小心。通过适当的使用,'printf'系列功能非常强大,并且是一个非常棒的工具。你的另一点更合理,但我认为你永远不需要将非平凡的数据传递给可变参数函数的可变参数部分。当然不是要调用'vfprintf'的人。 – paddy