printf()详解
1.printf()簡介
printf()是C語言標(biāo)準(zhǔn)庫函數(shù),用于將格式化后的字符串輸出到標(biāo)準(zhǔn)輸出。標(biāo)準(zhǔn)輸出,即標(biāo)準(zhǔn)輸出文件,對應(yīng)終端的屏幕。printf()申明于頭文件stdio.h。
函數(shù)原型:
int printf ( const char * format, ... );返回值:
正確返回輸出的字符總數(shù),錯誤返回負(fù)值,與此同時,輸入輸出流錯誤標(biāo)志將被置值,可由指示器ferror來檢查輸入輸出流的錯誤標(biāo)志。
調(diào)用格式:
printf()函數(shù)的調(diào)用格式為:printf(“格式化字符串”,輸出表列)。
格式化字符串包含三種對象,分別為:
(1)字符串常量;
(2)格式控制字符串;
(3)轉(zhuǎn)義字符。
字符串常量原樣輸出,在顯示中起提示作用。輸出表列中給出了各個輸出項,要求格式控制字符串和各輸出項在數(shù)量和類型上應(yīng)該一一對應(yīng)。其中格式控制字符串是以%開頭的字符串,在%后面跟有各種格式控制符,以說明輸出數(shù)據(jù)的類型、寬度、精度等。
注:本文的所有示例代碼均在Linux環(huán)境下以g++ 4.4.6編譯成64位程序的執(zhí)行。
2.格式控制字符串詳解
printf的格式控制字符串組成如下:
%[flags][width][.prec][length]type分別為:
%[標(biāo)志][最小寬度][.精度][類型長度]類型。2.1類型(type)
首先說明類型,因為類型是格式控制字符串的重中之重,是必不可少的組成部分,其它的選項都是可選的。type用于規(guī)定輸出數(shù)據(jù)的類型,含義如下:
| d/i | int | 輸出十進(jìn)制有符號32bits整數(shù),i是老式寫法 | printf("%i",123);輸出123 |
| o | unsigned int | 無符號8進(jìn)制(octal)整數(shù)(不輸出前綴0) | printf(“0%o”,123);輸出0173 |
| u | unsigned int | 無符號10進(jìn)制整數(shù) | printf("%u",123);輸出123 |
| x/X | unsigned int | 無符號16進(jìn)制整數(shù),x對應(yīng)的是abcdef,X對應(yīng)的是ABCDEF(不輸出前綴0x) | printf(“0x%x 0x%X”,123,123);輸出0x7b 0x7B |
| f/lf | float(double) | 單精度浮點數(shù)用f,雙精度浮點數(shù)用lf(printf可混用,但scanf不能混用) | printf("%.9f %.9lf",0.000000123,0.000000123);輸出0.000000123 0.000000123。注意指定精度,否則printf默認(rèn)精確到小數(shù)點后六位 |
| F | float(double) | 與f格式相同,只不過 infinity 和 nan 輸出為大寫形式。 | 例如printf("%f %F %f %F\n",INFINITY,INFINITY,NAN,NAN);輸出結(jié)果為inf INF nan NAN |
| e/E | float(double) | 科學(xué)計數(shù)法,使用指數(shù)(Exponent)表示浮點數(shù),此處”e”的大小寫代表在輸出時“e”的大小寫 | printf("%e %E",0.000000123,0.000000123);輸出1.230000e-07 1.230000E-07 |
| g | float(double) | 根據(jù)數(shù)值的長度,選擇以最短的方式輸出,%f或%e | printf("%g %g",0.000000123,0.123);輸出1.23e-07 0.123 |
| G | float(double) | 根據(jù)數(shù)值的長度,選擇以最短的方式輸出,%f或%E | printf("%G %G",0.000000123,0.123);輸出1.23E-07 0.123 |
| c | char | 字符型。可以把輸入的數(shù)字按照ASCII碼相應(yīng)轉(zhuǎn)換為對應(yīng)的字符 | printf("%c\n",64)輸出A |
| s | char* | 字符串。輸出字符串中的字符直至字符串中的空字符(字符串以空字符’\0‘結(jié)尾) | printf("%s",“測試test”);輸出:測試test |
| S | wchar_t* | 寬字符串。輸出字符串中的字符直至字符串中的空字符(寬字符串以兩個空字符’\0‘結(jié)尾) | setlocale(LC_ALL,“zh_CN.UTF-8”);wchar_t wtest[]=L"測試Test";printf("%S\n",wtest);輸出:測試test |
| p | void* | 以16進(jìn)制形式輸出指針 | printf("%010p",“l(fā)vlv”);輸出:0x004007e6 |
| n | int* | 什么也不輸出。%n對應(yīng)的參數(shù)是一個指向signed int的指針,在此之前輸出的字符數(shù)將存儲到指針?biāo)傅奈恢?/td> | int num=0;printf(“l(fā)vlv%n”,&num);printf(“num:%d”,num);輸出:lvlvnum:4 |
| % | 字符% | 輸出字符‘%’(百分號)本身 | printf("%%");輸出:% |
| m | 無 | 打印errno值對應(yīng)的出錯內(nèi)容 | printf("%m\n"); |
| a/A | float(double) | 十六進(jìn)制p計數(shù)法輸出浮點數(shù),a為小寫,A為大寫 | printf("%a %A",15.15,15.15);輸出:0x1.e4ccccccccccdp+3 0X1.E4CCCCCCCCCCDP+3 |
注意:
1)使用printf輸出寬字符時,需要使用setlocale指定本地化信息并同時指明當(dāng)前代碼的編碼方式。除了使用%S,還可以使用%ls。
(2)%a和%A是C99引入的格式化類型,采用十六進(jìn)制p計數(shù)法輸出浮點數(shù)。p計數(shù)法類似E科學(xué)計數(shù)法,但不同。數(shù)以0x開頭,然后是16進(jìn)制浮點數(shù)部分,接著是p后面是以 2為底的階碼。以上面輸出的15.15為例,推算輸出結(jié)果。15.15轉(zhuǎn)換成二進(jìn)制為1111.00 1001 1001 1001 1001 …,因為二進(jìn)制表示數(shù)值的離散特點,計算機(jī)對于小數(shù)有時是不能精確表示的,比如0.5可以精確表示為,而0.15卻不能精確表示。將15.15對應(yīng)的二進(jìn)制右移三位,為1.1110 0100 1100 1100 1100 …轉(zhuǎn)換對應(yīng)的十六進(jìn)制就是0x1.e4ccccccccccd,注意舍入時向高位進(jìn)了1位。由于右移三位,所以二進(jìn)制階碼就是3。最后的結(jié)果就是0x1.e4ccccccccccdp+3。
(3)格式控制字符串除了指明輸出的數(shù)據(jù)類型,還可以包含一些其它的可選的格式說明,依序有 flags, width, .precision and length。下面一一講解。
2.2標(biāo)志(flags)
flags規(guī)定輸出樣式,取值和含義如下:
| - | 減號 | 結(jié)果左對齊,右邊填空格。默認(rèn)是右對齊,左邊填空格。 |
| + | 加號 | 輸出符號(正號或負(fù)號) |
| space | 空格 | 輸出值為正時加上空格,為負(fù)時加上負(fù)號 |
| # | 井號 | type是o、x、X時,增加前綴0、0x、0X。type是a、A、e、E、f、g、G時,一定使用小數(shù)點。默認(rèn)的,如果使用.0控制不輸出小數(shù)部分,則不輸出小數(shù)點。type是g、G時,尾部的0保留。 |
| 0 | 數(shù)字零 | 將輸出的前面補(bǔ)上0,直到占滿指定列寬為止(不可以搭配使用“-”) |
示例:
printf("%5d\n",1000); //默認(rèn)右對齊,左邊補(bǔ)空格 printf("%-5d\n",1000); //左對齊,右邊補(bǔ)空格printf("%+d %+d\n",1000,-1000); //輸出正負(fù)號printf("% d % d\n",1000,-1000); //正號用空格替代,負(fù)號輸出printf("%x %#x\n",1000,1000); //輸出0xprintf("%.0f %#.0f\n",1000.0,1000.0)//當(dāng)小數(shù)點后不輸出值時依然輸出小數(shù)點printf("%g %#g\n",1000.0,1000.0); //保留小數(shù)點后后的0printf("%05d\n",1000); //前面補(bǔ)02.3輸出最小寬度(width)
用十進(jìn)制整數(shù)來表示輸出的最少位數(shù)。若實際位數(shù)多于指定的寬度,則按實際位數(shù)輸出,若實際位數(shù)少于定義的寬度則補(bǔ)以空格或0。width的可能取值如下:
| 數(shù)值 | 十進(jìn)制整數(shù) | printf("%06d",1000);輸出:001000 |
| * | 星號。不顯示指明輸出最小寬度,而是以星號代替,在printf的輸出參數(shù)列表中給出 | printf("%0*d",6,1000);輸出:001000 |
2.4精度(.precision)
| .數(shù)值 | 十進(jìn)制整數(shù)。(1)對于整型(d,i,o,u,x,X),precision表示輸出的最小的數(shù)字個數(shù),不足補(bǔ)前導(dǎo)零,超過不截斷。(2)對于浮點型(a, A, e, E, f ),precision表示小數(shù)點后數(shù)值位數(shù),默認(rèn)為六位,不足補(bǔ)后置0,超過則截斷。(3)對于類型說明符g或G,表示可輸出的最大有效數(shù)字。(4)對于字符串(s),precision表示最大可輸出字符數(shù),不足正常輸出,超過則截斷。precision不顯示指定,則默認(rèn)為0 |
| .* | 以星號代替數(shù)值,類似于width中的*,在輸出參數(shù)列表中指定精度。 |
示例:
printf("%.8d\n",1000); //不足指定寬度補(bǔ)前導(dǎo)0,效果等同于%06d printf("%.8f\n",1000.123456789);//超過精度,截斷 printf("%.8f\n",1000.123456); //不足精度,補(bǔ)后置0 printf("%.8g\n",1000.123456); //最大有效數(shù)字為8位 printf("%.8s\n",“abcdefghij”); //超過指定長度截斷輸出結(jié)果:
00001000 1000.12345679 1000.12345600 1000.1235 abcdefgh2.5類型長度(length)
類型長度指明待輸出數(shù)據(jù)的長度。因為相同類型可以有不同的長度,比如整型有16bits的short int,32bits的int,也有64bits的long int,浮點型有32bits的單精度float和64bits的雙精度double。為了指明同一類型的不同長度,于是乎,類型長度(length)應(yīng)運而生,成為格式控制字符串的一部分。
因為Markdown表格不支持單元格合并,背景顏色等樣式,所以直接引用printf.C++ reference的表格。
注意:黃色背景行標(biāo)識的類型長度說明符和相應(yīng)的數(shù)據(jù)類型是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輸出結(jié)果:
65 193 32767 65535 9223372036854775807 18446744073709551615注意:
long int到底是32bits還是64bits跟生成的程序是32bits還是64bits一一對應(yīng),如果使用g++編譯程序的話,可通過-m32或-m64選項分別生成32bits和64bits的程序。因本人測試代碼編譯生成的是64bits的程序,所以long int也就是64btis。
3.轉(zhuǎn)義字符
轉(zhuǎn)義字符在字符串中會被自動轉(zhuǎn)換為相應(yīng)操作命令。printf()使用的常見轉(zhuǎn)義字符如下:
| \a | 警報(響鈴)符 |
| \b | 回退符 |
| \f | 換頁符 |
| \n | 換行符 |
| \r | 回車符 |
| \t | 橫向制表符 |
| \v | 縱向制表符 |
| \ | 反斜杠 |
| \” | 雙引號 |
4.關(guān)于printf緩沖
在printf的實現(xiàn)中,在調(diào)用write之前先寫入IO緩沖區(qū),這是一個用戶空間的緩沖。系統(tǒng)調(diào)用是軟中斷,頻繁調(diào)用,需要頻繁陷入內(nèi)核態(tài),這樣的效率不是很高,而printf實際是向用戶空間的IO緩沖寫,在滿足條件的情況下才會調(diào)用write系統(tǒng)調(diào)用,減少IO次數(shù),提高效率。
printf在glibc中默認(rèn)為行緩沖,遇到以下幾種情況會刷新緩沖區(qū),輸出內(nèi)容:
(1)緩沖區(qū)填滿;
(2)寫入的字符中有換行符\n或回車符\r;
(3)調(diào)用fflush手動刷新緩沖區(qū);
(4)調(diào)用scanf要從輸入緩沖區(qū)中讀取數(shù)據(jù)時,也會將輸出緩沖區(qū)內(nèi)的數(shù)據(jù)刷新。
可使用setbuf(stdout,NULL)關(guān)閉行緩沖,或者setbuf(stdout,uBuff)設(shè)置新的緩沖區(qū),uBuff為自己指定的緩沖區(qū)。也可以使用setvbuf(stdout,NULL,_IOFBF,0);來改變標(biāo)準(zhǔn)輸出為全緩沖。全緩沖與行緩沖的區(qū)別在于遇到換行符不刷新緩沖區(qū)。
printf在VC++中默認(rèn)關(guān)閉緩沖區(qū),輸出時會及時的輸?shù)狡聊弧H绻@示開啟緩沖區(qū),只能設(shè)置全緩沖。因為微軟閉源,所以無法研究printf函數(shù)的實現(xiàn)源碼。
Linux和Windows下的緩沖區(qū)管理可見:C的全緩沖、行緩沖和無緩沖
5.printf與wprintf不能同時使用
該小結(jié)寫在2018年1月15日。兩年后的今日,在網(wǎng)上苦苦搜索尋求答案,終于解決了之前的疑惑。
在輸出寬字符串時,發(fā)現(xiàn)將printf和wprintf同時使用時,則后使用的函數(shù)沒有輸出。這里建議不要同時使用printf和wprintf,以免發(fā)生錯誤。
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:無任何內(nèi)容輸出 }上面的代碼中語句1和語句二不能同時存在,否則只能正常輸出第一個。也不知道在Windows平臺是否也存在這種問題,有興趣的讀者可以嘗試一下。關(guān)于原因,GNU官方文檔中有明確說明不能同時使用printf與wprintf,參見The GNU C Library Section 12.6 Streams in Internationalized Applications,內(nèi)容如下:
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.這里是因為輸出流在被創(chuàng)建時,不存在流定向,一旦使用了printf(多字節(jié)流)或wprintf(寬字符流)后,就被設(shè)置為對應(yīng)的流定向,且無法更改。可以使用如下函數(shù)獲取當(dāng)前輸出流的流定向。
// //@param:stream:文件流;mode:取值可以>0,=0或<0 //@ret:<0:流已被設(shè)置為多字節(jié)流定向;=0:流尚未被設(shè)置;>0:流已被設(shè)置為寬字符流定向 // int fwide (FILE* stream, int mode);//獲取當(dāng)前標(biāo)準(zhǔn)輸出流定向 int ret=fwide(stdout,0);通過fwide可以設(shè)置當(dāng)前流定向,前提是未有任何的I/O操作,也就是當(dāng)前流尚未被設(shè)置任何流定向。順帶吐槽一下,不知為何標(biāo)準(zhǔn)庫函數(shù)fwide為何實現(xiàn)的如此受限。具體操作如下:
//設(shè)置標(biāo)準(zhǔn)輸出流定向為多字節(jié)流定向 fwide(stdout,-1);//設(shè)置標(biāo)準(zhǔn)輸出流定向為寬字符流定向 fwide(stdout,1);既然GNU C存在這個問題,那該如何解決呢?這里有兩種辦法:
(1)統(tǒng)一使用一種函數(shù)。
例如:
或
printf("a\n"); printf("%ls\n",L"b");2)使用freopen清空流定向。
//重新打開標(biāo)準(zhǔn)輸出流,清空流定向 FILE* pFile=freopen("/dev/tty","w",stdout); wprintf(L"wide freopen succeeded\n");//重新打開標(biāo)準(zhǔn)輸出流,清空流定向 pFile=freopen("/dev/tty","w",stdout); printf("narrow freopen succeeded\n");上面可以讓printf與wprintf同時使用。
總結(jié)
以上是生活随笔為你收集整理的printf()详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WriteFile和GetSystemD
- 下一篇: MultiByteToWideChar和