淺解C語言的標準輸入輸出、標準錯誤

來源:互聯網
上載者:User

我們都知道用printf( xxx )和fprintf( stdin, xxx )是一個效果。所以stdin是一個FILE*類型的變數。同樣的stdout和stderr也是。

他們的定義在stdio.h(GNU實現)裡,其中部分代碼如下:

/* Standard streams.  */extern struct _IO_FILE *stdin;          /* Standard input stream.  */extern struct _IO_FILE *stdout;         /* Standard output stream.  */extern struct _IO_FILE *stderr;         /* Standard error output stream.  */#ifdef __STDC__/* C89/C99 say they're macros.  Make them happy.  */#define stdin stdin#define stdout stdout#define stderr stderr#endif

規範要求這幾個變數必需是宏,所以後面跟了幾個宏,當然這麼做是有風險的,例如如下代碼:

int main() {        setbuf( stdout, NULL );        fprintf( stdout, "hello, " );        stdout = stdout + 1;        fprintf( stdout, "world! ");        return 0;}

編譯不會有問題,但是運行時段錯誤。後來我又看了一下微軟的標準庫代碼,是這樣的:

#define stdin  (&__iob_func()[0])#define stdout (&__iob_func()[1])#define stderr (&__iob_func()[2])

從一個函數傳回值中取地址,這時stdout就不是左值了,是禁止被賦值的,編譯時間就會出錯。

在VC6時代,微軟似乎也是用了一個靜態數組_iob[],直接從這個數組中取值,這和GNU實現方法差不多,都有被改變值的危險。參看:http://blog.csdn.net/wanglei5695312/article/details/5402607

其實FILE是一個結構體_IO_FILE的別名宏,裡麵包含了檔案描述符號,讀寫指標位移值等資訊,在libio.h中可以找到:

struct _IO_FILE {  int _flags;           /* High-order word is _IO_MAGIC; rest is flags. */#define _IO_file_flags _flags  /* The following pointers correspond to the C++ streambuf protocol. */  /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */  char* _IO_read_ptr;   /* Current read pointer */  char* _IO_read_end;   /* End of get area. */  char* _IO_read_base;  /* Start of putback+get area. */  char* _IO_write_base; /* Start of put area. */  char* _IO_write_ptr;  /* Current put pointer. */  char* _IO_write_end;  /* End of put area. */  char* _IO_buf_base;   /* Start of reserve area. */  char* _IO_buf_end;    /* End of reserve area. */  /* The following fields are used to support backing up and undo. */  char *_IO_save_base; /* Pointer to start of non-current get area. */  char *_IO_backup_base;  /* Pointer to first valid character of backup area */  char *_IO_save_end; /* Pointer to end of non-current get area. */  struct _IO_marker *_markers;  struct _IO_FILE *_chain;  int _fileno;#if 0  int _blksize;#else  int _flags2;#endif  _IO_off_t _old_offset; /* This used to be _offset but it's too small.  */#define __HAVE_COLUMN /* temporary */  /* 1+column number of pbase(); 0 is unknown. */  unsigned short _cur_column;  signed char _vtable_offset;  char _shortbuf[1];  /*  char* _save_gptr;  char* _save_egptr; */    _IO_lock_t *_lock;#ifdef _IO_USE_OLD_IO_FILE};

我們知道,linux作業系統都是通過檔案描述符號(一個整數值)來確定對檔案的api調用的,而windows是通過維護一個[檔案描述符,控制代碼]的map,直接通過檔案描述符擷取核心控制代碼的,具體參看:http://www.cnblogs.com/fullsail/archive/2012/10/21/2732873.html

這個值就是上面結構體中的_fileno,那麼stdin、stdout、stderr對應的檔案描述符號分別是多少呢?我們來看unistd.h中的一段代碼:

/* Standard file descriptors.  */#define STDIN_FILENO    0       /* Standard input.  */#define STDOUT_FILENO   1       /* Standard output.  */#define STDERR_FILENO   2       /* Standard error output.  */

另外,提一下stdout和stderr的輸出模式的區別,預設情況下,stdout上綁定了一個緩衝區,會在遇到分行符號‘\n’或緩衝區滿時輸出,而stderr是立即輸出。

一個intel面試題是寫出下列代碼輸出:

int main() {    fprintf( stdout, "hello, " );    fprintf( stderr, "world! ");    return 0;}

輸出應該是“world!hello, ”,當然可以通過setbuf/setvbuf函數來改變一個FILE*對應的緩衝區。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.