5

我无法找到以下问题的答案,并且遇到了一些与功能相关的问题。

我的主要编程是用 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 调用会将格式化的字符串写入流(文件),这就足够了吗?

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

编辑:在 nos 发表评论后 - 我追踪了对今天经常导致此错误的函数的特定调用。

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

我确实认为这可能会引起一些麻烦,因为“TimerRestore[%d], Name=%s”后面应该跟着一个小数和字符串argumtn,但是只给出了一个字符串参数。我需要做一些测试,但我确定编写此代码的作者的意思是:

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

但是我仍然不明白为什么函数调用似乎并不总是导致错误。或者可能是 FileLog 函数中的“ReEntrant”变量在它没有失败时阻止它的原因?

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

4

4 回答 4

8

vprintf()(和朋友)允许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

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

于 2013-01-16T12:33:21.737 回答
2

首先,在 C++ 中使用fprintf()and (尤其是)是邪恶的。vfprintf()

要点:fprintf()是一个可变参数函数,它接受任意数量的参数。在内部,可变参数函数是通过使用 和 来“解包”可变参数来实现va_list的。va_start()va_end()

vfprintf()当您在解压缩自己的可变参数(即,您可以访问实例)fprintf()从您自己的可变参数函数中访问 的功能时使用。不是可变;它接受存储参数。va_listvfprintf()va_list

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

于 2013-01-16T12:38:44.470 回答
0

vfprintf允许您拥有可变参数列表,这意味着您可以在运行时动态创建参数列表。

于 2013-01-16T12:33:26.197 回答
0

这对于日志记录非常常见。您想制作一个 printf 样式的日志消息,例如:

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

但这需要可变参数。所以你需要vfprintf. 使用的原因fprintf也是因为它想要写入日期/时间戳,并且您不能将额外的内容添加到传递给vfprintf.

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

于 2013-01-16T12:36:44.627 回答