2012-06-14 69 views
3

我正在瀏覽一些舊的C代碼(下面列出),以便在新項目中重新使用它,並且我意識到我已經放棄了最終返回語句。特殊的事情是,例程工作完美,並沒有返回正確的文件指針。任何人都可以向我解釋爲什麼這是?從C函數返回FILE指針時的奇怪行爲

FILE* openforwrite(char *name, int binary) 
{ 
    //broken out from main to keep it tidy and allow multiple output files. 
    FILE *file; 
    //first see if it exists 
    file = fopen(name,"r"); 
    if (file) 
    { // it does, delete it 
     fclose(file); 
     if(remove(name)) bail("Output file already exists and cannot be deleted."); 
    } 
    //now lets re-create and open it for writing 
    if (binary) 
     file = fopen(name, "wb"); 
    else 
     file = fopen(name, "w"); 

    //check it actually opened 
    if (!file) 
     bail("Error opening output file."); 

    //and pass the pointer back 
    return file; // <-- I had omitted this line initially but the routine still worked 
} 
+0

您可以在文件上使用'stat()'來查看它是否存在,而不是打開/關閉。請注意,您的代碼很活潑,如果在關鍵應用程序中使用,可能會讓某些人使用計時攻擊來代替指定的文件名滑入符號鏈接,並讓您的應用程序在應該塗寫的地方塗寫數據。 –

+2

I'米驚訝它編譯 - 似乎是一個非void返回類型的函數不返回任何不會編譯。 – jedwards

+0

謝謝Marc,這是一個很好的觀點,下次使用時我會這樣做。 – Tyr

回答

3

fopen調用的返回值最終會將文件句柄放在通常用於返回值的寄存器中(例如,eax)。如果在函數退出前沒有任何更改該寄存器值,它仍然可以爲調用者提供。例如,如果在fopen之後和功能結束之前再有一個函數調用,那麼它可能會覆蓋寄存器eax,並且確定失敗。正如其他人所說,這是未定義的行爲。儘管如此,它最初是一個非常令人費解的情況(而且很有趣)。

0

的問題是,你調用未定義行爲。它似乎可以工作,可以崩潰,並且在緩衝區溢出的情況下,它可以刪除文件。

1

它只是碰巧工作。編譯器使用堆棧將參數傳遞給函數,並接收它們的返回值。它還在計算過程中使用本地變量和臨時變量的堆棧。當函數返回時,調用者查看堆棧中特定位置的返回值。該值恰好包含文件指針值,因爲這是函數計算的最後一個表達式。

在任何情況下都不要認爲這會再次以這種方式工作。

由於許多原因,包括不同的編譯器版本,不同的優化,或僅僅因爲編譯器隨機決定教你一堂課,它可能會中斷。