C言語の文法的な意味においては
ファイル記述子はファイルポインタが参照する構造体(ファイルシステムのiノードリストに含まれる利用者ファイル記述子表の構造体)の持つ要素のうちの一つ(int _fileno)です。
FILE型の定義を調べてみます。
$ less -N /usr/include/stdio.h
48 /* The opaque type of streams. This is the definition used elsewhere. */
49 typedef struct _IO_FILE FILE;
なるほど。48-49行目のところでFILE型の実体は、_IO_FILEという構造体であることがわかります。
_IO_FILEは、stdio.hがインクルードしているヘッダファイルの/usr/include/libio.hに記述されています。それを見てみます。
$ less -N /usr/include/libio.h
271 struct _IO_FILE {
272 int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
273 #define _IO_file_flags _flags
274
275 /* The following pointers correspond to the C++ streambuf protocol. */
276 /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
277 char* _IO_read_ptr; /* Current read pointer */
278 char* _IO_read_end; /* End of get area. */
279 char* _IO_read_base; /* Start of putback+get area. */
280 char* _IO_write_base; /* Start of put area. */
281 char* _IO_write_ptr; /* Current put pointer. */
282 char* _IO_write_end; /* End of put area. */
283 char* _IO_buf_base; /* Start of reserve area. */
284 char* _IO_buf_end; /* End of reserve area. */
285 /* The following fields are used to support backing up and undo. */
286 char *_IO_save_base; /* Pointer to start of non-current get area. */
287 char *_IO_backup_base; /* Pointer to first valid character of backup area */
288 char *_IO_save_end; /* Pointer to end of non-current get area. */
289
290 struct _IO_marker *_markers;
291
292 struct _IO_FILE *_chain;
293
294 int _fileno;
295 #if 0
296 int _blksize;
297 #else
298 int _flags2;
299 #endif
300 _IO_off_t _old_offset; /* This used to be _offset but it's too small. */
301
302 #define __HAVE_COLUMN /* temporary */
303 /* 1+column number of pbase(); 0 is unknown. */
304 unsigned short _cur_column;
305 signed char _vtable_offset;
306 char _shortbuf[1];
307
308 /* char* _save_gptr; char* _save_egptr; */
309
310 _IO_lock_t *_lock;
311 #ifdef _IO_USE_OLD_IO_FILE
312 };
なんだか壮大なスケールの構造体の定義でしたが、目的のint_filenoを見つけ出すことができました。
ここで、あらかじめ定義されているファイルポインタstdin, stdout, stderrの定義内容を見てみます。
$ less -N /usr/include/stdio.h
164 /* Standard streams. */
165 extern struct _IO_FILE *stdin; /* Standard input stream. */
166 extern struct _IO_FILE *stdout; /* Standard output stream. */
167 extern struct _IO_FILE *stderr; /* Standard error output stream. */
なるほど。stdin, stdout, stderrは_IO_FILE型の構造体を参照するポインタとして定義されているのですね。
以上のことより、下のようなプログラムを作ることができます。
// test.c
#include<stdio.h> /* printf, stdin, stdout, stderr */
int main(void)
{
//ファイルポインタの宣言
FILE *fp;
fp = fopen("./inputfile", "r");
/*各々のファイルポインタが参照するFILE型の構造体の_filenoを
表示させる。
_filenoとはファイルシステムのiノードリスト内に存在する
iノードリトにある、「利用者ファイル記述表」というFILE型の構造体
が連結されたリストの順番を示す番号である。 */
/*stdin, stout, stderrはstdio.hで定義されている
ファイルポインタである。*/
printf("stdin : %d\n", stdin->_fileno);
printf("stdout : %d\n", stdout->_fileno);
printf("stderr : %d\n", stderr->_fileno);
printf("fp : %d\n", fp->_fileno);
//ファイルのクローズ
close(fp);
return 0;
}
#コンパイル
$ gcc -o test test.c
#実行
$ ./test
stdin : 0
stout : 1
stderr: 2
Segmentation fault
#inputfileという空ファイルを作成
$ touch inputfile
#3番のファイル記述子がちゃんと割り当てられていることが確認できる。
$ ./test
stdin : 0
stout : 1
stderr: 2
fp : 3