Я не смог найти ответ на следующий вопрос, и у меня возникли некоторые проблемы, связанные с функциями.
Мое основное программирование выполняется на 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), и этого будет достаточно?
Небольшое объяснение или некоторая информация будет высоко ценится 🙂
РЕДАКТИРОВАТЬ: После комментария от nos — я отследил конкретный вызов функции, которая сегодня часто вызывает эту ошибку.
FileLog( "TimerRestore[%d], Name=%s", Package.CurGame->Timers[ Index ].Name.c_str() );
Я действительно думаю, что это может вызвать некоторые проблемы, так как «TimerRestore [% d], Name =% s» должно сопровождаться десятичной дробью и строкой arguemtn, однако указывается только строковый аргумент. Мне нужно провести некоторое тестирование, но я уверен, что автор, который написал этот код, хотел написать:
FileLog( "TimerRestore[%d], Name=%s", Index, Package.CurGame->Timers[ Index ].Name.c_str() );
Однако я до сих пор не понимаю, почему вызов функции не всегда приводит к ошибке. Или, может быть, причина того, что переменная «ReEntrant» в функции FileLog блокирует ее, когда она не выходит из строя?
Большое спасибо за все отзывы и информацию.
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
Я не знаю, почему ваша функция приводит к ошибке. Ваш фрагмент кода не предоставляет достаточно деталей. Количество типов предоставляемых аргументов функции может быть быть причиной.
Во-первых, используя fprintf()
и особенно) vfprintf()
в С ++ есть злой.
К точке: fprintf()
является переменной функцией, она принимает произвольное количество аргументов. Внутренне, переменные функции реализуются путем «распаковки» аргументов с использованием va_list
, va_start()
а также va_end()
,
vfprintf()
используется, когда вы хотите получить доступ к функциональности fprintf()
от вашей собственной вариационной функции после Вы распаковали свои собственные аргументы (то есть, у вас есть доступ к va_list
пример). vfprintf()
является не VARIADIC; он принимает va_list
хранение аргументов.
Вы не разместили объявление вызова вашей функции fprintf()
а также vfprintf()
, но мы можем предположить, что это вариативно. Сначала использует fprintf()
распечатать некоторые данные в LogFile
, а затем использует vfprintf()
печатать свои собственные аргументы variadic там.
vfprintf
Позволяет вам иметь список переменных аргументов Это означает, что вы можете динамически создавать список параметров во время выполнения.
Это довольно часто для регистрации. Вы хотите создать сообщение журнала в стиле printf, например:
Log("The value of x is now %d", x);
Но это требует переменных аргументов. Так вам нужно vfprintf
, Причина fprintf
используется также потому, что он хочет написать отметку даты / времени, и вы не можете добавить этот дополнительный материал в существующий формат, переданный в vfprintf
,
Другой способ сделать это — использовать строковую версию vsprintf
и сделайте одну большую строку, затем запишите ее в файл. Но это более подвержено ошибкам (например, переполнение буфера).