C语言入门基础之输入和输出
標準輸入函數
在stdio.h中scanf聲明如下:
/* Read formatted input from stdin.This function is a possible cancellation point and therefore notmarked with __THROW. */ extern?int?scanf?(const?char?*__restrict __format, ...) __wur;使用Mac或Linux的同學,在終端上輸入man scanf回車即可學習scanf函數的用法。我們可以看到注釋上說明,scanf從標準輸入stdin輸入讀取數據,在glibc中stdin的定義如下:
/*stdio.c*/ FILE *stdin?= (FILE *) &_IO_2_1_stdin_; /*libio.h*/ extern?struct?_IO_FILE_plus?_IO_2_1_stdin_; /*libioP.h*/ struct?_IO_FILE_plus {FILE file;const?struct?_IO_jump_t?*vtable; };從以上代碼我們可以知道,最終stdin是一個FILE文件流指針,我能繼續追蹤FILE類型為何物。
/** stdio state variables.** The following always hold:** if (_flags&(__SLBF|__SWR)) == (__SLBF|__SWR),* _lbfsize is -_bf._size, else _lbfsize is 0* if _flags&__SRD, _w is 0* if _flags&__SWR, _r is 0** This ensures that the getc and putc macros (or inline functions) never* try to write or read from a file that is in `read' or `write' mode.* (Moreover, they can, and do, automatically switch from read mode to* write mode, and back, on "r+" and "w+" files.)** _lbfsize is used only to make the inline line-buffered output stream* code as compact as possible.** _ub, _up, and _ur are used when ungetc() pushes back more characters* than fit in the current _bf, or when ungetc() pushes back a character* that does not match the previous one in _bf. When this happens,* _ub._base becomes non-nil (i.e., a stream has ungetc() data iff* _ub._base!=NULL) and _up and _ur save the current values of _p and _r.** NB: see WARNING above before changing the layout of this structure!*/ typedef??struct?__sFILE?{unsigned?char?*_p;?/* current position in (some) buffer */int??_r;?/* read space left for getc() */int??_w;?/* write space left for putc() */short??_flags;?/* flags, below; this FILE is free if 0 */short??_file;?/* fileno, if Unix descriptor, else -1 */struct??__sbuf?_bf;??/* the buffer (at least 1 byte, if !NULL) */int??_lbfsize;?/* 0 or -_bf._size, for inline putc *//* operations */void??*_cookie;?/* cookie passed to io functions */int??(* _Nullable _close)(void?*);int??(* _Nullable _read) (void?*,?char?*,?int);fpos_t??(* _Nullable _seek) (void?*,?fpos_t,?int);int??(* _Nullable _write)(void?*,?const?char?*,?int);/* separate buffer for long sequences of ungetc() */struct??__sbuf?_ub;??/* ungetc buffer */struct?__sFILEX?*_extra;?/* additions to FILE to not break ABI */int??_ur;?/* saved _r when _r is counting ungetc data *//* tricks to meet minimum requirements even when malloc() fails */unsigned?char?_ubuf[3];?/* guarantee an ungetc() buffer */unsigned?char?_nbuf[1];?/* guarantee a getc() buffer *//* separate buffer for fgetln() when line crosses buffer boundary */struct??__sbuf?_lb;??/* buffer for fgetln() *//* Unix stdio files get aligned to block boundaries on fseek() */int??_blksize;?/* stat.st_blksize (may be != _bf._size) */fpos_t??_offset;?/* current lseek offset (see WARNING) */ } FILE;看到這個結構體內部一大堆成員變量不要慌,我們重點關注里面的close、read、seek和write函數指針。我們在調用scanf函數時正是通過這幾個函數指針間接調用系統函數close、read、seek和write實現標準輸入關閉、讀取、偏移和寫功能。
int??(* _Nullable _close)(void?*); int??(* _Nullable _read) (void?*,?char?*,?int); fpos_t??(* _Nullable _seek) (void?*,?fpos_t,?int); int??(* _Nullable _write)(void?*,?const?char?*,?int);從函數聲明我們知道scanf返回一個int型返回值,在調用時scanf,返回正整數表示從標準輸入讀取到的有效數據數量,返回0表示沒有輸入或者輸入不正確,返回負數表示發生了從標準輸入讀取數據發生了錯誤。下面我們使用scanf從標準輸入讀取數據的代碼。
int?num =?0; float?f_num =?0;int?count =?scanf("%d", &num); scanf("%f", &f_num);scanf_s("%d", &num);在scanf中輸入數據并將數據保存在變量num和f_num中,調用scanf輸入數據必須要用%,%d表示輸入一個整數,%f表示輸入一個單精度浮點數,count保存scanf輸入數據的有效數。
在C語言里函數傳參方式有2種,一種是傳值另外一種是傳指針。通過傳值方式形參拷貝實參,得到一個實參副本對實參副本進行修改不會影響實參,而傳指針方式,將會得到實參的地址,通過指針解引用可以間接修改實參的值。那么回到scanf函數那里,我們通過對變量進行取址,scanf函數內部有一個指針,將變量地址值賦給內部指針,再將標準輸入的值賦值給實參,實參變量因此獲得標準輸入的值。
標準輸出函數
在stdio.h中printf函數聲明如下:
/* Write formatted output to stdout.This function is a possible cancellation point and therefore notmarked with __THROW. */ extern?int?printf?(const?char?*__restrict __format, ...);看到這里是不是很熟悉?printf函數的返回值也是int型,調用printf函數將會返回輸出字符個數,出錯則返回一個負數。同樣在Linux/Mac平臺的終端上輸入man printf函數可以查看函數的詳細使用方法(任何C標準函數都可以在Linux/Mac平臺上輸入man+函數名的方式查看函數使用方法)。下面是我們使用printf函數在標準輸出中輸出數據的代碼。
int?output_count =?printf("num = %d\n", num); printf("output_count = %d\n", output_count);output_count =?printf("f_num = %f\n", f_num); printf("output_count = %d\n", output_count);在代碼片段里我們看到一個\n字符,在C語言里這是一個換行符??吹竭@里是不是又有疑問了,為什么printf函數輸出變量值時不需要對變量取地址?這就回到前面我們說過的問題了,在C語言里傳值,形參是實參的副本,形參修改了不會影響到實參。而printf函數只是在標準輸出中輸出信息,不會修改實參的值,因此使用傳值方式。
那么標準輸出是什么呢?從print函數聲明代碼注釋上看,標準輸出正是stdou,我們繼續在glibc中繼續追蹤stdout到底是什么?在stdout.c中我們看到stdout和stderr定義如下:
FILE *stdout?= (FILE *) &_IO_2_1_stdout_; FILE *stderr?= (FILE *) &_IO_2_1_stderr_;我們發現stdout、stderr和stdin的定義一模一樣都是一個FILE類型指針,那么使用方式就和stdin一樣了,區別則在于stdin和文件描述符0綁定,stdout和文件描述符1綁定,stderr和文件描述符2綁定。
聲明:
本文于網絡整理,版權歸原作者所有,如來源信息有誤或侵犯權益,請聯系我們刪除或授權事宜。
總結
以上是生活随笔為你收集整理的C语言入门基础之输入和输出的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 苹果iPhone临时关闭Face ID人
- 下一篇: C 桥接模式 - 开关和电器