printf()是C語言標準庫函數,用於將格式化後的字符串輸出到標準輸出。標準輸出,即標準輸出文件,對應終端的屏幕。printf()申明於頭文件stdio.h。html
函數原型:node
int printf ( const char * format, ... );
返回值:
正確返回輸出的字符總數,錯誤返回負值。與此同時,輸入輸出流錯誤標誌將被置值,可由指示器函數ferror(FILE *stream)來檢查輸入輸出流的錯誤標誌,若是ferror()返回一個非零值,表示出錯。c++
調用格式:
printf()函數的調用格式爲:printf("格式化字符串",輸出表列)
。微信
格式化字符串包含三種對象,分別爲:
(1)字符串常量;
(2)格式控制字符串;
(3)轉義字符。
字符串常量原樣輸出,在顯示中起提示做用。輸出表列中給出了各個輸出項,要求格式控制字符串和各輸出項在數量和類型上應該一一對應。其中格式控制字符串是以%開頭的字符串,在%後面跟有各類格式控制符,以說明輸出數據的類型、寬度、精度等。app
**注:**本文的全部示例代碼均在Linux環境下以g++ 4.4.6編譯成64位程序的執行。less
printf的格式控制字符串組成以下:ide
%[flags][width][.prec][length]type
分別爲:函數
%[標誌][最小寬度][.精度][類型長度]類型。
首先說明類型,由於類型是格式控制字符串的重中之重,是必不可少的組成部分,其它的選項都是可選的。type用於規定輸出數據的類型,含義以下:測試
字符 | 對應數據類型 | 含義 | 示例 |
---|---|---|---|
d/i | int | 輸出十進制有符號32bits整數,i是老式寫法 | printf("%i",123); 輸出123 |
o | unsigned int | 無符號8進制(octal)整數(不輸出前綴0) | printf("0%o",123); 輸出0173 |
u | unsigned int | 無符號10進制整數 | printf("%u",123); 輸出123 |
x/X | unsigned int | 無符號16進制整數,x對應的是abcdef,X對應的是ABCDEF(不輸出前綴0x) | printf("0x%x 0x%X",123,123); 輸出0x7b 0x7B |
f/lf | float(double) | 單精度浮點數用f,雙精度浮點數用lf(printf可混用,但scanf不能混用) | printf("%.9f %.9lf",0.000000123,0.000000123); 輸出0.000000123 0.000000123。注意指定精度,不然printf默認精確到小數點後六位 |
F | float(double) | 與f格式相同,只不過 infinity 和 nan 輸出爲大寫形式。 | 例如printf("%f %F %f %F\n",INFINITY,INFINITY,NAN,NAN); 輸出結果爲inf INF nan NAN |
e/E | float(double) | 科學計數法,使用指數(Exponent)表示浮點數,此處"e"的大小寫表明在輸出時「e」的大小寫 | printf("%e %E",0.000000123,0.000000123); 輸出1.230000e-07 1.230000E-07 |
g | float(double) | 根據數值的長度,選擇以最短的方式輸出,%f或%e | printf("%g %g",0.000000123,0.123); 輸出1.23e-07 0.123 |
G | float(double) | 根據數值的長度,選擇以最短的方式輸出,%f或%E | printf("%G %G",0.000000123,0.123); 輸出1.23E-07 0.123 |
c | char | 字符型。能夠把輸入的數字按照ASCII碼相應轉換爲對應的字符 | printf("%c\n",64) 輸出A |
s | char* | 字符串。輸出字符串中的字符直至字符串中的空字符(字符串以空字符’\0‘結尾) | printf("%s","測試test"); 輸出:測試test |
S | wchar_t* | 寬字符串。輸出字符串中的字符直至字符串中的空字符(寬字符串以兩個空字符’\0‘結尾) | setlocale(LC_ALL,"zh_CN.UTF-8"); wchar_t wtest[]=L"測試Test"; printf("%S\n",wtest); 輸出:測試test |
p | void* | 以16進制形式輸出指針 | printf("0x%p","lvlv"); 輸出:0x000000013FF73350 |
n | int* | 什麼也不輸出。%n對應的參數是一個指向signed int的指針,在此以前輸出的字符數將存儲到指針所指的位置 | int num=0; printf("lvlv%n",&num); printf("num:%d",num); 輸出:lvlvnum:4 |
% | 字符% | 輸出字符‘%’(百分號)自己 | printf("%%"); 輸出:% |
m | 無 | 打印errno值對應的出錯內容 | printf("%m\n"); |
a/A | float(double) | 十六進制p計數法輸出浮點數,a爲小寫,A爲大寫 | printf("%a %A",15.15,15.15); 輸出:0x1.e4ccccccccccdp+3 0X1.E4CCCCCCCCCCDP+3 |
注意:
(1)使用printf輸出寬字符時,須要使用setlocale指定本地化信息並同時指明當前代碼的編碼方式。除了使用%S,還可使用%ls。
(2)%a和%A是C99引入的格式化類型,採用十六進制p計數法輸出浮點數。p計數法相似E科學計數法,但不一樣。數以0x開頭,而後是16進制浮點數部分,接着是p後面是以 2爲底的階碼。以上面輸出的15.15爲例,推算輸出結果。15.15轉換成二進制爲1111.00 1001 1001 1001 1001 ...
,由於二進制表示數值的離散特色,計算機對於小數有時是不能精確表示的,好比0.5能夠精確表示爲0.12ui
0.12,而0.15卻不能精確表示。將15.15對應的二進制右移三位,爲1.1110 0100 1100 1100 1100 ...
轉換對應的十六進制就是0x1.e4ccccccccccd,注意舍入時向高位進了1位。因爲右移三位,因此二進制階碼就是3。最後的結果就是0x1.e4ccccccccccdp+3。
(3)格式控制字符串除了指明輸出的數據類型,還能夠包含一些其它的可選的格式說明,依序有 flags, width, .precision and length。下面一一講解。
flags規定輸出樣式,取值和含義以下:
字符 | 名稱 | 說明 |
---|---|---|
- | 減號 | 結果左對齊,右邊填空格。默認是右對齊,左邊填空格。 |
+ | 加號 | 輸出符號(正號或負號) |
space | 空格 | 輸出值爲正時加上空格,爲負時加上負號 |
# | 井號 | type是o、x、X時,增長前綴0、0x、0X。 type是a、A、e、E、f、g、G時,必定使用小數點。默認的,若是使用.0控制不輸出小數部分,則不輸出小數點。 type是g、G時,尾部的0保留。 |
0 | 數字零 | 將輸出的前面補上0,直到佔滿指定列寬爲止(不能夠搭配使用「-」) |
示例:
printf("%5d\n",1000); //默認右對齊,左邊補空格 printf("%-5d\n",1000); //左對齊,右邊補空格 printf("%+d %+d\n",1000,-1000); //輸出正負號 printf("% d % d\n",1000,-1000); //正號用空格替代,負號輸出 printf("%x %#x\n",1000,1000); //輸出0x printf("%.0f %#.0f\n",1000.0,1000.0)//當小數點後不輸出值時依然輸出小數點 printf("%g %#g\n",1000.0,1000.0); //保留小數點後後的0 printf("%05d\n",1000); //前面補0
輸出結果爲:
用十進制整數來表示輸出的最少位數。若實際位數多於指定的寬度,則按實際位數輸出,若實際位數少於定義的寬度則補以空格或0。width的可能取值以下:
width | 描述 | 示例 |
---|---|---|
數值 | 十進制整數 | printf("%06d",1000); 輸出:001000 |
* | 星號。不顯示指明輸出最小寬度,而是以星號代替,在printf的輸出參數列表中給出 | printf("%0*d",6,1000); 輸出:001000 |
精度格式符以「.」開頭,後跟十進制整數。可取值以下:
.precision | 描述 |
---|---|
.數值 | 十進制整數。 (1)對於整型(d,i,o,u,x,X),precision表示輸出的最小的數字個數,不足補前導零,超過不截斷。 (2)對於浮點型(a, A, e, E, f ),precision表示小數點後數值位數,默認爲六位,不足補後置0,超過則截斷。 (3)對於類型說明符g或G,表示可輸出的最大有效數字。 (4)對於字符串(s),precision表示最大可輸出字符數,不足正常輸出,超過則截斷。 precision不顯示指定,則默認爲0 |
.* | 以星號代替數值,相似於width中的*,在輸出參數列表中指定精度。 |
示例:
printf("%.8d\n",1000); //不足指定寬度補前導0,效果等同於%06d printf("%.8f\n",1000.123456789);//超過精度,截斷 printf("%.8f\n",1000.123456); //不足精度,補後置0 printf("%.8g\n",1000.123456); //最大有效數字爲8位 printf("%.8s\n",「abcdefghij」); //超過指定長度截斷
輸出結果:
00001000 1000.12345679 1000.12345600 1000.1235 abcdefgh
注意: 在對浮點數和整數截斷時,存在四捨五入。
類型長度指明待輸出數據的長度。由於相同類型能夠有不一樣的長度,好比整型有16bits的short int,32bits的int,也有64bits的long int,浮點型有32bits的單精度float和64bits的雙精度double。爲了指明同一類型的不一樣長度,因而乎,類型長度(length)應運而生,成爲格式控制字符串的一部分。
由於Markdown表格不支持單元格合併,背景顏色等樣式,因此直接引用printf.C++ reference的表格。
**注意:**黃色背景行標識的類型長度說明符和相應的數據類型是C99引入的。
示例代碼:
printf("%hhd\n",'A'); //輸出有符號char printf("%hhu\n",'A'+128); //輸出無符號char printf("%hd\n",32767); //輸出有符號短整型short int printf("%hu\n",65535); //輸出無符號短整型unsigned short int printf("%ld\n",0x7fffffffffffffff); //輸出有符號長整型long int printf("%lu\n",0xffffffffffffffff); //輸出有符號長整型unsigned long int
輸出結果:
65 193 32767 65535 9223372036854775807 18446744073709551615
注意:
long int究竟是32bits仍是64bits跟生成的程序是32bits仍是64bits一一對應,若是使用g++編譯程序的話,可經過-m32
或-m64
選項分別生成32bits和64bits的程序。因本人測試代碼編譯生成的是64bits的程序,因此long int也就是64btis。
轉義字符在字符串中會被自動轉換爲相應操做命令。printf()使用的常見轉義字符以下:
轉義字符 | 意義 |
---|---|
\a | 警報(響鈴)符 |
\b | 回退符 |
\f | 換頁符 |
\n | 換行符 |
\r | 回車符 |
\t | 橫向製表符 |
\v | 縱向製表符 |
\\ | 反斜槓 |
" | 雙引號 |
#4.關於printf緩衝
在printf的實現中,在調用write以前先寫入IO緩衝區,這是一個用戶空間的緩衝。系統調用是軟中斷,頻繁調用,須要頻繁陷入內核態,這樣的效率不是很高,而printf實際是向用戶空間的IO緩衝寫,在知足條件的狀況下才會調用write系統調用,減小IO次數,提升效率。
printf在glibc中默認爲行緩衝,遇到如下幾種狀況會刷新緩衝區,輸出內容:
(1)緩衝區填滿;
(2)寫入的字符中有換行符\n
或回車符\r
;
(3)調用fflush手動刷新緩衝區;
(4)調用scanf要從輸入緩衝區中讀取數據時,也會將輸出緩衝區內的數據刷新。
可以使用setbuf(stdout,NULL)
關閉行緩衝,或者setbuf(stdout,uBuff)
設置新的緩衝區,uBuff爲本身指定的緩衝區。也可使用setvbuf(stdout,NULL,_IOFBF,0);
來改變標準輸出爲全緩衝。全緩衝與行緩衝的區別在於遇到換行符不刷新緩衝區。
printf在VC++中默認關閉緩衝區,輸出時會及時的輸到屏幕[3]
[3]。若是顯示開啓緩衝區,只能設置全緩衝。由於微軟閉源,因此沒法研究printf函數的實現源碼。
Linux和Windows下的緩衝區管理可見:C的全緩衝、行緩衝和無緩衝。
該小結寫在2018年1月15日。兩年後的今日,在網上苦苦搜索尋求答案,終於解決了以前的疑惑。
在輸出寬字符串時,發現將printf和wprintf同時使用時,則後使用的函數沒有輸出。這裏建議不要同時使用printf和wprintf,以避免發生錯誤。
printf和wprintf不能同時輸出寬字符串的示例代碼以下:
#include <stdio.h> #include <wchar.h> #include <locale.h> int main(int argc,char* argv[]) { char test[]="測試Test"; setlocale(LC_ALL,"zh_CN.UTF-8"); wchar_t wtest[]=L"0m~K0m~UTest"; printf("printf:%S\n",wtest); //語句1:可正常輸出"測試Test" wprintf(L"wprintf:%S\n",wtest); //語句2:無任何內容輸出 }
上面的代碼中語句1和語句二不能同時存在,不然只能正常輸出第一個。也不知道在Windows平臺是否也存在這種問題,有興趣的讀者能夠嘗試一下。關於緣由,GNU官方文檔中有明確說明不能同時使用printf與wprintf,參見The GNU C Library Section 12.6 Streams in Internationalized Applications,內容以下:
It is important to never mix the use of wide and not wide operations on a stream. There are no diagnostics issued. The application behavior will simply be strange or the application will simply crash.
這裏是由於輸出流在被建立時,不存在流定向,一旦使用了printf(多字節流)或wprintf(寬字符流)後,就被設置爲對應的流定向,且沒法更改。可使用以下函數獲取當前輸出流的流定向。
// //@param:stream:文件流;mode:取值能夠>0,=0或<0 //@ret:<0:流已被設置爲多字節流定向;=0:流還沒有被設置;>0:流已被設置爲寬字符流定向 // int fwide (FILE* stream, int mode); //獲取當前標準輸出流定向 int ret=fwide(stdout,0);
經過fwide能夠設置當前流定向,前提是未有任何的I/O操做,也就是當前流還沒有被設置任何流定向。順帶吐槽一下,不知爲什麼標準庫函數fwide爲什麼實現的如此受限。具體操做以下:
//設置標準輸出流定向爲多字節流定向 fwide(stdout,-1); //設置標準輸出流定向爲寬字符流定向 fwide(stdout,1);
既然GNU C存在這個問題,那該如何解決呢?這裏有兩種辦法:
(1)統一使用一種函數。
例如:
wprintf(L"%s","a\n"); wprintf(L"b\n");
或
printf("a\n"); printf("%ls\n",L"b");
(2)使用freopen清空流定向。
//從新打開標準輸出流,清空流定向 FILE* pFile=freopen("/dev/tty","w",stdout); wprintf(L"wide freopen succeeded\n"); //從新打開標準輸出流,清空流定向 pFile=freopen("/dev/tty","w",stdout); printf("narrow freopen succeeded\n");
上面可讓printf與wprintf同時使用。
耗時將近兩年,終於完成了此篇看似基礎,但卻紛繁複雜的printf()用法。因爲時間和我的水平有限,文章不足之處在所不免,也請讀者批評指正,不甚感激。
[1]淺談C中的wprintf和寬字符顯示
[2]printf.C++ reference
[3]Why does printf not flush after the call unless a newline is in the format string?
[4]格式規範語法:printf 和 wprintf 函數
[5]The GNU C Library Section 12.6 Streams in Internationalized Applications
[6]fwide.C++ reference