C 語言中函數 fopen 所打開的文件指針指向的文件到底是什麼?

例如

FILE *fout = fopen("a.out", "w");

此時 *fout 的值是多少,代表了何種含義呢?

------------------------------------------------------------------------------------------------------------------

在stdio.h中查到了如下代碼:

typedef struct _iobuf
{
char* _ptr;
int _cnt;
char* _base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
} FILE;

這個問題其實是在操作系統課上教授提出的,他詢問了以下代碼的運行結果:

#include &

int main(){

FILE *fout1 = fopen("a.out","w");

FILE *fout2 = fopen("b.out","w");

printf("%d
%d
", *fout1, *fout2);

return 0;

}

他認為結果應該為兩個較小的Interger並且鼓勵我們課後找到答案,然而我在實際嘗試過程中並不能得到他所說的答案(只有隨機的大數字)。所以我的希望是從文件在系統中的存儲方式和fopen函數本身來解釋這個問題。

---------------------------------

問題已經解決,感謝各位的回答。

File Descriptor 文件描述符 就是我想要的答案。


你這個問題問的太大了,要真詳細講的話,可以把整個操作系統的文件系統部分都介紹一下才行。

簡單點說FILE就是一個結構體,FILE*就是一個指針。

這個結構體的內容在不同的編譯環境下是不同的(即使是同一個操作系統的不同編譯器,可能也是不同的)。

這個結構體具體包括什麼東西,只取決於你的編譯器的標準庫是如何設計的。

你給出的這個,貌似是VC的定義:

struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;

這是我隨手找的一個別的開源(非Linux)的FILE的定義:

struct file_struct
{
int fs_filedes; /* File descriptor associated with stream */
#if CONFIG_STDIO_BUFFER_SIZE &> 0
sem_t fs_sem; /* For thread safety */
pid_t fs_holder; /* Holder of sem */
int fs_counts; /* Number of times sem is held */
FAR unsigned char *fs_bufstart; /* Pointer to start of buffer */
FAR unsigned char *fs_bufend; /* Pointer to 1 past end of buffer */
FAR unsigned char *fs_bufpos; /* Current position in buffer */
FAR unsigned char *fs_bufread; /* Pointer to 1 past last buffered read char. */
#endif
uint16_t fs_oflags; /* Open mode flags */
uint8_t fs_flags; /* Stream flags */
#if CONFIG_NUNGET_CHARS &> 0
uint8_t fs_nungotten; /* The number of characters buffered for ungetc */
unsigned char fs_ungotten[CONFIG_NUNGET_CHARS];
#endif
};

可見不同的標準庫里,FILE的定義差異是很大的

標準庫是什麼東西?就是你平時使用C語言編程時用的stdio.h stdlib.h里定義的各種結構、函數的具體實現,它實現了不同操作系統的統一封裝

在Windows里,open一個文件的真正API是CreateFile,在Linux里可以認為是open,因為訪問文件是一個與底層相關的操作,所以不同操作系統對文件操作的具體定義可能是完全不同的。C語言標準庫就是在這種不同的定義之上,完成一套封裝,實現統一介面訪問。

具體到你這個環境(VC)里,以VC6為例,它的open的源碼如下:

FILE * __cdecl _tfsopen (
const _TSCHAR *file,
const _TSCHAR *mode
,int shflag
)
{
REG1 FILE *stream;
REG2 FILE *retval;

_ASSERTE(file != NULL);
_ASSERTE(*file != _T(""));
_ASSERTE(mode != NULL);
_ASSERTE(*mode != _T(""));

/* Get a free stream */
/* [NOTE: _getstream() returns a locked stream.] */

if ((stream = _getstream()) == NULL)
return(NULL);

/* open the stream */
#ifdef _UNICODE
retval = _wopenfile(file,mode,shflag,stream);
#else /* _UNICODE */
retval = _openfile(file,mode,shflag,stream);
#endif /* _UNICODE */

/* unlock stream and return. */
_unlock_str(stream);
return(retval);
}

它內部調用_openfile實現真正的打開文件的功能,具體到訪問WindowsAPI之前的調用棧是:

fopen-&>_openfile-&>_tsopen-&>CreateFile

完整代碼由於太長,我就不貼了,MSDN上有很多。

具體到這個結構體的定義來說,從我讀過的一部分代碼的理解是:

_cnt成員是表示這個FILE結構_ptr成員里有多少有效的數據,如果用戶試圖讀/寫的話,會把從/向_ptr中操作。如果_cnt里的數據比用戶請求的數據要少的時候,會調用_read/_write讀寫更多的數據,而這兩個操作最終都是調用ReadFile/WriteFile這兩個標準的Windows API,至於這些標準API背後的東西,因為涉及到文件系統的具體實現,這是非常複雜的。

如果你要完全理解標準庫的實現,建議閱讀MSDN里附帶的CRT部分的源碼。

------------------------------

你通過printf輸出的數字是FILE第一個成員指針的值,其實就是一個地址,這個地址具體是什麼數值,是不確定的,不同操作系統、編譯環境差異很大,它只是一個地址值,你如果自己定義一個結構體,通過malloc申請一段內存,也可以列印出一堆你認為比較奇怪的數字,這很正常。

他認為結果應該為兩個較小的Interger並且鼓勵我們課後找到答案,然而我在實際嘗試過程中並不能得到他所說的答案(只有隨機的大數字)

我不認為他的這種說法是對的,一般來說,FILE結構體都是通過malloc申請的,在Windows平台上堆上數據一般都不會太小(至少都會大於0x400000),而如果要獲得很小的數字,我能猜測到的可能是Linux上POSIX標準的API,這些API在open的時候可能會返回一個非常小的數值,但這不是標準庫的返回值(這是open不是fopen)。所以你們老師的說法有待考證。


沒猜錯的話你老師要說的是File Descriptor。

但那個是open返回的的,不是C標準庫裡面的fopen。

所以糾結個卵。有時間還不如去看看怎麼實現一個文件系統。


根據結構看,指向的是兩個字元串指針,當然是較大的值。你老師錯了。


FILE是一個struct啊,我怎麼覺得應當會報編譯時錯誤,因為使用了非基本類型作為可變參函數的參數?


小數字是unix的文件描述符,

輸出指針是地址,不可能小,


UNIX環境高級編程

第3章 文件I/O

第5章 標準I/O庫


你們老師把系統庫的open函數和c庫的fopen搞混了,open函數是系統調用,返回int類型文件描述符,每個進程初始有三個默認打開的文件描述符,0,1,2,分別代表stdin,stdou,stderr,你用第一次open打開的文件描述符應該為3,fopen是c庫針對open的一個封裝,結構里的__file,應該就是文件描述符。


在支持POSIX環境下肯定有文件描述符!


操作系統內部維護一個文件打開列表,列表的內個打開條目描述的是文件在內存中的位置,名稱,磁碟上的位置,對該文件的引用之類的信息,很全面。每個進程也有個文件打開表,裡面的每一個條目指向操作系統內部文件打開表中打開的那一個文件描述結構。。。詳細介紹你可以看一下操作系統概念這本書文件系統那一章節。


你老師說的應該是文件描述符吧

標準輸入 0

標準輸出 1

標準出錯 2

以後再打開的就是3 4 5 ...


操作系統課上教授……詢問了以下代碼的運行結果:

printf("%d
%d
", *fout1, *fout2);

你們的教授完全不懂C語言,

你也是。

那是C代碼嗎?

*fout1 是什麼?能用%d轉換輸出?

回去重學C語言!

不要問這種無聊且根本不成立的問題。

他認為結果應該為兩個較小的Interger並且鼓勵我們課後找到答案

荒唐。


代碼裡面打開是兩個指針,所以很大,用系統調用打開文件的話才會是兩個小的int,老師應該是記錯了:)


推薦閱讀:

Android系統可以高度定製化,但為什麼那些專業遊戲機(例如3DS,PS4,PS Vita,Wii U)不用此作為主機操作系統?
為什麼微軟這樣的大公司也分不清 KB 和 KiB?
計算機專業本科生、鍵盤黨,覬覦linux的高效性,想棄win投Linux,求合適的發行版本?
你所知的計算機相關的最難的項目或項目類別是什麼?

TAG:操作系統 | C編程語言 |