2013-01-16 114 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