讀古今文學網 > C語言解惑 > 8.4 充分利用庫函數printf的功能 >

8.4 充分利用庫函數printf的功能

由於篇幅的限制,一般的教材只是以能滿足基本編程為前提,簡要介紹printf的基本功能。其實,printf函數還有許多有用的功能,本節除分析使用printf函數容易出現的問題之外,也將介紹一些其他功能,如添加修飾符和標誌、使用替換符等。

【例8.8】能給出下面程序的正確輸出結果嗎?


#include <stdio.h>
int  main 
( 
)
{
      char c="this % is good
!"
;
      int a[3]={1
,3
,-5}
;
      printf 
( "%s\n"
, c 
);     //1
      printf 
( c 
);     //2
      printf 
( "\n10%%\n"
);     //3
      printf 
( "%d\n"
, printf 
( "How are you
?\n"
));     //4
      printf 
( "%.6d %06d\n"
, a[0
,1]
,a[1
,2]
);     //5
      printf 
( "-10%% is %.1f\n"
,-0.1
);     //6
      return 0
;
}
  

【解答】第1條輸出語句是打印出任何以空字符結尾的字符串,而%是作為組成字符串的一個字符,所以語法沒有歧義。第2個輸出語句是將字符串c中的任何%字符作為格式說明的一部分,其後的字符被視為格式字符,但%後是空格和字符i,不能構成有效的格式說明,所以帶來麻煩,無法判定它將會輸出什麼。第3個輸出字符串中的「%%」是正確的,它用來輸出一個「%」號,所以輸出「10%」。

如果知道printf函數是int類型,也就能給出第4個輸出語句的輸出結果。它先輸出字符串,然後把打印的個數(有\n則再加1)作為另一個printf語句的參數(輸出13)。

第5個打印語句的輸出量表使用了逗號運算符,a[0,1]和a[1,2]分別是a[1]和a[2]。其餘的事情就是分析格式字符的含義。「.6」和「06」的含義一樣,都是要求輸出寬度為6位,因為a[2]是-5,所以只填4個0。

第6個很容易,輸出小數為1位,即輸出「-10%is-0.1」。下面給出在一特定機器上的輸出結果供對照分析。


this % is good
!
this  2367460s good
!
10%
How are you
?
13
000003 -00005
-10% is -0.1
  

由此可見,要掌握printf的格式控制符的使用方法。為節省篇幅,本節以介紹使用方法及注意事項為主。

8.4.1 printf的函數原型


int printf
(const char *format
,...
);
  

...表示參數可變,printf的參數個數根據format來的,將在第2篇編寫自己的printf函數時再介紹,現在僅僅知道它是可變參數,第1個參數是字符串即可。為此,將其簡化為如下的一般格式。


printf
(格式控制符,輸出量表);
  

格式控制符是用雙引號("")括起來的字符串,也稱控制字符串,它包括以下信息。

(1)格式說明由「%」和格式字符組成,如%d,%f等,它的作用是將輸出的數據轉換為指定格式輸出。格式說明總是由「%」字符開始的。

(2)普通字符,即需要原樣輸出的字符。

(3)輸出量表是需要輸出的一些數據,也可以是表達式。

本節主要針對格式控制符,目的是用好printf提供的功能。在格式說明中,格式字符不一定要緊跟在%號之後,把緊跟%號之後的格式說明稱為簡單格式類型,把簡單類型中的%號與格式字符中添加一些可選字符,這些可選字符可以各種方式修改轉換,根據其作用可以分為添加修飾及標誌等情況。為了查閱方便,下面將按此組成方式予以敘述。

8.4.2 printf函數的格式控制符

1.printf的函數的簡單格式控制符

簡單格式控制符有:%d、%o、%x、%X、%s、%c、%g、%f、%e、%%、%p和%n。

C語言取消了一些控制字符,也新增了一些,因為各個編譯系統執行情況不一樣,如果吃不準,最好不要使用已經取消的控制符。當然,可以測試一下它們的效果,以免進入歧途。

注意大寫的格式字符,不要使用%D、%O、%U、%S和%P。

%u只用來輸出正整數,也可以用來輸出內存地址。

%d、%o、%x、%X分別輸出10、8和16進制的整數,輸出正整數時,不打印「+」標識符。%x和%X是有區別的,前者輸出小寫字符,後者輸出大寫字符。%d輸出負數使用「-」號,8進制和16進制則不使用「-」,而是輸出轉換後的正整數。

%p和%n是新引入的格式字符。%p用於打印指針所指向的地址(16進制),或者內存儲器地址值。%n用於將打印字符個數存入指定變量。注意計數字符數是指%n之前的數目。


int k=0
;
char c="OK"
;
printf 
("%s%n\n"
, c
,&k 
);     //\n
在%n
後面,不記入,k=2
printf 
("%d\n"
, k 
);     //
輸出2
printf 
("%s\n %n "
, c
,&k 
);     //\n
在%
前面要記入,k=3
printf 
("%d\n"
, k 
);     //
輸出3
  

因為是存入整數,所以使用地址符&,這與scanf讀入數據的格式相同。下面給出一個演示正確和錯誤使用格式字符的例子,請對比它們的輸出掌握其用法。

【例8.9】演示簡單格式字符使用的例子。


#include <stdio.h>
int  main 
( 
)
{
     int n=200
,i
,j
,k
; 
     char c="OK"
;
     printf 
("%d %o %x %X %O %D\n"
, n
,n
,n
,n
,n
,n
);     //O
和D
不用
     printf 
( "%x %X %o %d\n"
,-1
,-1
,-1
,-1
);          //o
和x
轉換為正數
     printf 
("%u %u %U\n"
, 55
,-55
,55 
);               //
僅用於正整數,不用U
     printf 
("%s %S\n"
, c
,c 
);                    //S
不可用
     printf 
("%c %C\n"
, c[0]
,c[1] 
);               //
均可
     printf 
("%p %u %P\n"
, &n
,&n
,&n 
);               //
不用P
,輸出地址
     printf 
("How are you
?%n"
, &i 
);               //i=12
     printf 
("%s%n\n"
, c
,&j 
);                    //\n
在%n
後面,不計入,j=2
     printf 
("%c\n%n"
, c[1]
,&k 
);                    //\n
在前面要記入,k=2
     printf 
( "i=%d
,j=%d
,k=%d\n"
, i
,j
,k
);
     printf 
("100%%\n"
);                         //
輸出%
     printf 
("%s%n\n"
, c
,&k 
);                    //\n
在%n
後面,不記入,k=2
     printf 
("%d\n"
, k 
);                         //
輸出k=2
     printf 
("%s\n%n"
, c
,&k 
);                    //\n
在前面要記入,k=2
     printf 
("%d\n"
, k 
);                         //\n
在前面要記入,k=2
     return 0
;
}
  

程序輸出結果如下:


200 310 c8 C8 O D
ffffffff FFFFFFFF 37777777777 -1
55 4294967241 U
OK
O K
0012FF7C 1245052 P
How are you
?OK
K
i=12
,j=2
,k=2
100%
OK
2
OK
3
  

%f、%e、%g用來輸出浮點值。%f格式強制禁止使用指數形式表示浮點數,小數點後面有6位有效數字,只能用小寫的f,不能用F。%e與%f正好相反,它要求一律顯式地使用指數形式,小數點後面也是保留6位有效數字。%g用來輸出實數,它根據數值的大小,自動選f格式或e格式。%g不是保留小數點後面6位有效數字,而是總共為6位數字,並且再打印浮點或雙精度類型數值時,也會去掉該數值尾綴的零。例如下例中的2.5。對於指數部分而言,規定並不相同。下面的例子中,指數總共佔5位(如e+007),其中「e」占1位,符號占1位,指數占3位。

【例8.10】演示使用簡單格式字符輸出浮點數的例子。


#include <stdio.h>
int  main 
( 
)
{
     float n=2.5
,k=15400440.0
,h=8.9808093f
; 
     printf 
("%f %e %E %g %G\n"
, n
,n
,n
,n
,n
,n
);
     printf 
("%f %e %g %G\n"
, k
,k
,k
,k
);
     printf 
("%f %e %g %G\n"
, h
,h
,h
,h
);
     printf 
("%g %g\n"
, 1.23456e-4
,1.23456e-5
); 
     printf 
("%g %g\n"
, 123456.0
,1234567.0
);
     return 0
;
}
  

程序輸出如下:


2.500000 2.500000e+000 2.500000E+000 2.5 2.5
15400440.000000 1.540044e+007 1.54004e+007 1.54004E+007
8.980809 8.980809e+000 8.98081 8.98081
0.000123456 1.23456e-005
123456 1.23457e+006
  

%g對大於999999的數和指數小於或等於-5的情況,才採用科學計數法輸出。由此可見,%g很適於打印那些不需要按列對齊的浮點數。

假如有一個單個字符c和字符串s,語句


printf
("%s"
,s
);
  

並不等效於語句


printf
(s
);
  

只有當字符串中沒有%號時,輸出才是一樣的。語句「printf(c);」是錯誤的,可以用


putchar
(c
);
  

代替它,但前者更加靈活。

printf語句的格式控制符可以分成多個書寫,只要雙引號內的語法正確即可,裡面還可以包含多個換行符\n。例8.11給出兩個例子。同理,輸出變量表不僅可以使用表達式求值,而且可以使用多個表達式求值。例如:


printf 
("there %s %d item%s in the list.\n"
,
       i
!=1
? "are" 
: "is"
, i
, i
!=1
? "s"
:" "
);
  

當i=3時,第1個表達式是"are",第2個是"s",printf變為


printf 
("there %s %d item%s in the list.\n"
, "are" 
, "s"
);
  

語句,輸出:


there are 3 items in the list.
  

對照下面的例子和輸出結果,可以熟悉它們的用法。

【例8.11】演示%c和%s的例子。


#include <stdio.h>
int  main 
( 
)
{
     char c='A'
, s="ABC"
; 
     int i=3
,j=1
;
     printf 
("%c"
,c
);
     printf 
(s
);
     printf 
("%s\n"
,s
);
     printf 
("we""%s CB%c\n"
,s
,c
);
     printf 
("THIS IS ""%s\nchar '%c' is %d\n"
,s
,c
,c
);
     printf 
("there %s %d item%s in the list.\n"
,
            i
!=1
? "are" 
: "is"
, i
, i
!=1
? "s"
:" "
);
     printf 
("there %s %d item%s in the list.\n"
,
            j
!=1
? "are" 
: "is"
, j
, j
!=1
? "s"
:" "
);
     return 0
;
}
  

程序輸出結果如下:


AABCABC
weABC CBA
THIS IS ABC
char 'A' is 65
there are 3 items in the list.
there is 1 item  in the list.
  

2.添加修飾符

在格式說明符%和格式字符之間加上輔助字符,可以構成長度修飾符、寬度修飾符和精度修飾符。

(1)長度修飾符l(或L,不分大小寫)用來構成%ld、%1o、%lx和%lu。整數有short、long和正常長度3種。儘管當一個short整數作為一個函數的參數出現時,會被自動地擴展為一個正常長度的整數,但仍然需要一種通知printf函數,某個參數是long型整數。l修飾符只對用於整數的格式字符才有意義。%lu仍然是把long型正整數作為long型無符號整數打印出來。

【例8.12】演示使用修飾符l的例子。


#include <stdio.h>
int  main 
( 
)
{
     long n=1234567898
; 
     printf 
("%ld\t%Ld\n"
, n
,n
);
     printf 
("%lo\t%lx\t%lX\n"
, n
,n
,n
);
     printf 
("%lu\t%lu\t\t%u\n"
, n
,&n
,&n
);
     return 0
;
}
  

程序輸出結果如下:


1234567898      1234567898
11145401332     499602da      499602DA
1234567898      1245052       1245052
  

(2)寬度修飾符用於在固定長度的域內打印數值,它處於「%」和格式字符之間,假設m是正整數,可以表示為「%m+格式字符」。m是指定要打印的字符數,又稱為域寬。如果要打印的數值(或字符)不能填滿m個位置,它的左側就會被補上空格以使這個數值(或字符)的寬度滿足要求。不過,寬度修飾符絕對不會截斷一個輸出域,因為一旦待打印的數值太大而超過給定的域寬m時,就會適當調整輸出域的寬度m以容納該數值。也就是說,若大於m,則按實際位數輸出。

在一些編譯系統中,寬度修飾符對所有的格式字符都有效,也有些編譯系統對「%%」無效,但編譯時並不給出警告信息(不影響產生執行文件)。

【例8.13】演示使用修飾符指定域寬寬度的例子。


#include <stdio.h>
int  main 
( 
)
{
     int num=25
;
     printf 
("%2d %2d\n"
, 1
,2
);     //1 
     printf 
("%2o %2X\n"
, 9
,15
);     //2
     printf 
("%2c %3c\n"
, 'a'
,'b'
);     //3
     printf 
("%8%%3%\n"
);     //4 
本編譯系統無效
     printf 
("%2d %2d\n"
, 97
,98
);     //5  
     printf 
("%2d %2d\n"
, 100
,99
);     //6
     printf 
("%2x %2d\n"
, 100
,99
);     //7
     printf 
("%2s %3s\n"
, "ab"
,"a b c d"
);     //8
     printf 
("%10p %10u\n"
, &num
, &num
);     //9
     printf 
("%2p %2u\n"
, &num
, &num
);     //10
     return 0
;
}
  

運行結果如下:


 1  2
11  f
 a   b
%%
97 98
100 99
64 99
ab a b c d
   0012FF7C    1245052
0012FF7C 1245052
  

從運行結果可見,不足寬度則以空格補充,如第1行的輸出結果。第6行自動調整寬度,不影響輸出100,第8行的字符串「a b c d」也是如此處理。第4行輸出「%」時,沒有用空格填充,表明本編譯系統不適合%%。第9行填充空格,而第10行自動調整不影響輸出。

(3)精度修飾符用於控制一個數值表示中將要出現的數字位數,或者用於限制將要打印的字符串中應該出現的字符數。假設n是一個正整數,精度修飾符可以表示為「.n」,即精度修飾符包括一個小數點和正整數n,可以表示為「%m+格式字符」。精度修飾符的確切含義與格式字符有關。在實際使用時,它還會與(2)中介紹的寬度修飾符m配合,下面將講解具體的使用方法和實例。

1對於整數%.nd、%.no、%.nx和%.nu(%.np特殊)來說,n指定了打印數字的最少位數。如果要打印的數值並不需要n位數字來表示,則在它的前面補0。如果也使用了寬度m,當m大於n時才起作用,它用空格補足m位。語句


printf 
("%.2d/%.2d/%.4d\n"
, 15
,5
,2014
);
printf 
("%4.3d/%4.2d/%4.4d\n"
, 15
,5
,2014
);
  

將輸出:


15/05/2014
 015/  05/2014
  

%p打印的是16進制地址,地址使用固定長度,書寫時不能隨便在地址前面補0。所以對於「%.np」而言,僅符合上述補空格的規律。例8.14演示了它們的區別。

2對於字符串,n指定了要從字符串中打印的字數。如果字符串中包含的字符數少於n,則僅將全部字符輸出(不在左邊填空格),如果字符串中包含的字符數大於n,則僅將全部字符中的前n個字符截斷輸出(捨去其他字符)。如果使用「%m.ns」的格式,當m≤n時,m不起作用,按「.n」處理。當m>n時,僅輸出字符中的前n個字符,字符左邊用m-n個空格填充以保持寬度為m個字符。這種方法在某些場合特別有用,例如用字符數組name[N]存儲文件名,如果恰巧文件名的長度為N,這就使name失去結束符。但可以使用「%.Ns」正確打印文件名。如果有必要,還可以在文件名左邊填充空格。例8.14給出示範。

3對於單字符,不理睬「%m.nc」的形式,一律輸出單字符,但也遵守在字符左邊填充空格的規律。

【例8.14】演示使用精度修飾符的例子及輸出結果。


#include <stdio.h>
int  main 
( 
)
{
     int num=25
,i=0
;
     char name[8]
;
     printf 
("%.2d/%.2d/%.2d\n"
, 15
,5
,2014
);
     printf 
("%4.3d/%4.2d/%3.2d\n"
, 15
,5
,2014
);
     printf 
("%.18%/%4.2%\n"
);
     printf 
("%.8s
,%.2s\n"
, "ABCDEF"
,"ABCDEF"
);
     printf 
("%2.4s
,%2.2s\n"
, "ABCDEF"
,"ABCDEF"
);
     printf 
("%8.4s
,%8.2s\n"
, "ABCDEF"
,"ABCDEF"
);
     printf 
("%.2p/%.2u\n"
, &num
,&num
);
     printf 
("%.18p/%.18u\n"
, &num
,&num
);
     printf 
("%12.2p/%12.2u\n"
, &num
, &num
);
     printf 
("%.18c/%4.2c\n"
, 'A'
,'A'
);
     for
(i=0
;i<8
;i++
)
     name[i]='a'+i
;
     printf 
("%.8s\n"
,name
);
     printf 
("%10.8s\n"
,name
);
     return 0
;
}
15/05/2014
 015/  05/2014
%/%
ABCDEF
,AB
ABCD
,AB
   ABCD
,        AB
0012FF7C/1245052
0012FF7C/000000000001245052
    0012FF7C/     1245052
A/   A
abcdefgh
  abcdefgh
  

4對於實數%.ne、%.nE、%.nx和%.nu來說,精度修飾符.n中的n指定了小數點後應該出現的數字位。只有精度大於0時,打印的數值中才會出現小數點。也就是說,「%.0f」和「%.0e」將數值按四捨五入取整打印,而且不打印小數點「.」。下面的語句


printf 
("%.0f %.0f %.0f %.1f %.1f\n"
, 1.
,2.2
,2.5
,2.5
,1.
);
printf 
("%.0e %.0e %.0E %.1E %.1E\n"
, 1.
,2.2
,2.5
,2.5
,1.
);
  

將給出如下輸出結果:


1 2 3 2.5 1.0
1e+000 2e+000 3E+000 2.5E+000 1.0E+000
  

如果給定小數點後面的數字小於n,則在尾部用「0」填充,使總寬度達到n位。

如果使用「m.n」配合時,當m大於給定實數小數點前面的位數,假設位數為i,在m>i+n時,左邊使用空格填充,使其整體占m寬度。注意負數的「-」也占一位。

其實,打印實數時,是需要左邊用空格,還是右邊補0均可。右邊補0是「.n」決定,左邊填空格是因為使用「m」引起。但e是個特例,它不採用空格,小數位數完全決定n。本節提供的例題,演示了各種情況,供對比以加深理解。如果記不得了,將這些例題再運行一下,就完全清楚了。下面是例8.15及其輸出結果,最後一行還給出e的典型情況。

【例8.15】演示實數使用精度修飾符的例子。


#include <stdio.h>
int  main 
( 
)
{
     float f=1234.567f
;
     printf 
("%.0f %.0f %.0f %.1f %.1f\n"
, 1.
,2.2
,2.5
,2.5
,1.
);
     printf 
("%.0e %.0e %.0E %.1E %.1E\n"
, 1.
,2.2
,2.5
,2.5
,1.
);
     printf 
("%4.2f
,%.3f
,%8.12f\n"
, 12345678976.2245
,0.2
,.2
);
     printf 
("%4.2f
,%10.2f\n"
, 12345.2245
,1232.5151
);
     printf 
("%4.2e
,%10.2e\n"
, 12345.2245
,1232.5151
);
     printf 
("%4.2e
,%.3e
,%8.12e\n"
, 12345678976.2245
,0.2
,.2
);
     printf 
("%8.2f
,%5.2f\n"
, -123.45
,-123.45
);
     printf 
("%8.2e
,%5.2e\n"
, -123.45
,-123.45
);
     printf 
( "%e
,%10e
,%10.2e
,%.2e\n"
,f
,f
,f
,f
);
     return 0
;
}
  

程序輸出結果如下:


1 2 3 2.5 1.0
1e+000 2e+000 3E+000 2.5E+000 1.0E+000
12345678976.22
,0.200
,0.200000000000
12345.22
,   1232.52
1.23e+004
, 1.23e+003
1.23e+010
,2.000e-001
,2.000000000000e-001
 -123.45
,-123.45
-1.23e+002
,-1.23e+002
1.234567e+003
,1.234567e+003
, 1.23e+003
,1.23e+003
  

5對於%.ng和%.nG來說,它與f和e添加精度修飾符的含義有所不同,這裡是指定打印數值中的有效數字位數n。如果小數點後面不跟數字,則小數點也將被刪除。請對比下面例題中的輸出結果以分辨它們的區別。

【例8.16】演示對比使用精度修飾符的例子。


#include <stdio.h>
int  main 
( 
)
{
     float f=1234.567f
;
     printf 
("%.0f %.0f %.0f %.1f %.1f\n"
, 1.
,2.2
,2.5
,2.5
,1.
);
     printf 
("%.0g %.0g %.0G %.1G %.1G\n"
, 1.
,2.2
,2.5
,2.5
,1.
);
     printf 
("%4.2f
,%.3f
,%8.12f\n"
, 12345678976.2245
,0.2
,.2
);
     printf 
("%4.2g
,%10.2g\n"
, 12345.2245
,1232.5151
);
     printf 
("%4.2e
,%10.2e\n"
, 12345.2245
,1232.5151
);
     printf 
("%4.2E
,%.3E
,%8.12E\n"
, 12345678976.2245
,0.2
,.2
);
     printf 
("%4.2G
,%.3G
,%8.12G\n"
, 12345678976.2245
,0.2
,.2
);
     printf 
("%8.2f
,%5.2f\n"
, -123.45
,-123.45
);
     printf 
("%8.2e
,%5.2e\n"
, -123.45
,-123.45
);
     printf 
("%8.2g
,%5.2g\n"
, -123.45
,-123.45
);
     printf 
( "%e
,%10e
,%10.2e
,%.2e\n"
,f
,f
,f
,f
);
     printf 
( "%g
,%10g
,%10.2g
,%.2g\n"
,f
,f
,f
,f
);
     return 0
;
}
 

程序輸出結果如下:


1 2 3 2.5 1.0
1 2 3 3 1
12345678976.22
,0.200
,0.200000000000
1.2e+004
,  1.2e+003
1.23e+004
, 1.23e+003
1.23E+010
,2.000E-001
,2.000000000000E-001
1.2E+010
,0.2
,     0.2
 -123.45
,-123.45
-1.23e+002
,-1.23e+002
-1.2e+002
,-1.2e+002
1.234567e+003
,1.234567e+003
, 1.23e+003
,1.23e+003
1234.57
,   1234.57
,  1.2e+003
,1.2e+003
  

3.添加標誌

可以在%符號和域寬修飾符m之間插入標誌字符,以微調格式字符的效果。常用的標誌為-、+、#和空白字符。

(1)標誌符「-」的作用是將顯示方式改為左端對齊,在右端填充空白字符。標誌符「-」僅當有域寬修飾符m存在時才有意義,而且m要大於數值寬度才起作用。在下面的例子中,每行均使用字符a作為右邊界標誌。

【例8.17】演示使用標誌符「-」的例子。


#include <stdio.h>
int  main 
( 
)
{
     float f=1234.567f
;
     char c="abcdefghijklmnop"
;
     int num=123456
;
     printf
("%s\n"
,c
);
     printf 
("%-4d
,%-4d
,%c\n"
, num
,num
, c[0]
);  //
域寬4
小於num
長度,不起作用
     printf 
("%-8d
,%-8d
,%c\n"
, num
,num
, c[0]
);
     printf 
("%-8d
,%-8d
,%c\n"
, -123
,-4567
,c[0]
);
     printf
("%-8.4s
,%-16.16s
,%c\n"
,c
,c
,c[0]
);
     printf 
("%-8.3g
,%-12.2g
,%c\n"
, 123.45
,123.45
,c[0]
);
     printf 
( "%-16e
,%-16e
,%c\n"
,f
,f
,c[0]
);
     printf 
( "%-16.2e
,%-16.2e
,%c\n"
,f
,f
,c[0]
);
    printf 
( "%-16g
,%-16g
,%c\n"
,f
,f
,c[0]
);
    printf 
( "%-16.2g
,%-16.2g
,%c\n"
,f
,f
,c[0]
);
    return 0
;
}
  

輸出結果如下:


abcdefghijklmnop
123456
,123456
,a
123456  
,123456  
,a
123456
,123456
,a
-123    
,-4567   
,a
abcd    
,abcdefghijklmnop
,a
123     
,1.2e+002    
,a
1.234567e+003   
,1.234567e+003   
,a
1.23e+003       
,1.23e+003       
,a
1234.57         
,1234.57         
,a
1.2e+003        
,1.2e+003        
,a
  

(2)標誌符「+」規定在打印數值時,都用數值的符號(正數使用「+」號,負數使用「-」號)作為第1個字符,而0作為+0打印。8和16進制對負數另有規定,所以對它們不能使用這個標誌符。

這個標誌符「+」與上面(1)中講述的標誌符「-」之間沒有任何關係,是分別獨立的標誌符。如果要同時使用它們,必須把「+」放在前面,例如%+-4d。下面給出它們的用法。

【例8.18】演示使用標誌符「+」的例子。


#include <stdio.h>
int  main 
( 
)
{
     double f[3]={1
,0
,-1}
;
     int n[3]={1
,0
, -23}
;
     printf
("%+d %+d %+d\n"
,n[0]
,n[1]
,n[2]
);
     printf
("%+-4d%+-4d %+-4d\n"
,n[0]
,n[1]
,n[2]
);
     printf
("%+g %+g %+g\n"
,f[0]
,f[1]
,f[2]
);
     printf
("%+-18g%+-18g %+-18g\n"
,f[0]
,f[1]
,f[2]
);
     printf
("%+e %+e %+e\n"
,f[0]
,f[1]
,f[2]
);
     printf
("%+-18e%+-18e%+-18e\n"
,f[0]
,f[1]
,f[2]
);
     printf
("%+d\n%+d\n"
,-123
,123
);
     return 0
;
}
  

程序運行結果如下:


+1 +0 -23
+1  +0   -23
+1 +0 -1
+1                 +0                   -1
+1.000000e+000 +0.000000e+000 -1.000000e+000
+1.000000e+000     +0.000000e+000     -1.000000e+000
-123
+123
  

(3)空白字符又稱空格("")符,也是標誌符之一。它的作用在正數和0之前不打印「+」字符,而是用空格代替「+」號。如例8.17輸出的最後兩行為例,雖然希望正負數據對齊,但不希望打印「+」號,這時就可以使用空格標誌符。由此可見,如果希望固定欄內的數值向左對齊,而又不想用標誌符「+」(即輸出有「-」號而沒有「+」號),則可使用空格標誌符。

注意:如果標誌符「+」與空格字符同時出現在控制說明中,最終的效果以標誌符「+」為準。

【例8.19】演示使用空格和「+」標誌符的例子。


#include <stdio.h>
int  main 
( 
)
{
     double f
;
     int i
;
     for
(i=-1
;i<2
;i++
)
           printf
("% d %+d %+ d\n"
,i
,i
,i
);
     for
(f=-1
;f<2
;f++
)
           printf
("% f %+f %+ f\n"
,f
,f
,f
);
     for
(f=-1
;f<2
;f++
)
           printf
("% e %+e %e\n"
,f
,f
,f
);
     return 0
;
}
  

程序運行結果如下:


-1 -1 -1
 0 +0 +0
 1 +1 +1
-1.000000 -1.000000 -1.000000
 0.000000 +0.000000 +0.000000
 1.000000 +1.000000 +1.000000
-1.000000e+000 -1.000000e+000 -1.000000e+000
 0.000000e+000 +0.000000e+000  0.000000e+000
 1.000000e+000 +1.000000e+000  1.000000e+000
  

從運行結果可看出「+」標誌和空格標誌的區別。當兩者相遇時,則決定「+」標誌。

用%e不能保證小數點對齊,而%e和%+e能很好地解決這個問題,所以這兩種格式要比正常的%e格式有用得多。

(4)標誌符「#」的作用是針對數值輸出的,而且具體的方式與特定的格式字符有關。

1%#o是讓輸出的第1個數字前加0,以便讓8進制數值輸出的格式與大多數C程序員慣用的方法一致。例如將10進制100輸出為0144。當然也可以使用0%o在輸出前加0,但這與%#o並不等效。當輸出8進制的0時,用%#o格式輸出0,而0%o輸出00。

2%#x和%#X在要打印的16進制數值前面分別加上0x和0X。

3標誌符「#」對浮點數輸出格式的影響之一是要求小數點必須被打印出來,即使小數點後面沒有數字也是如此。

4標誌符「#」對浮點數輸出格式的另一個影響是針對%g和%G的。%g會把尾綴的0去掉,例如將5.0打印為5,而%#g則打印出尾綴的0,即輸出5.00000。

不要混淆宏定義中「#」字符的作用。printf語句中的「#」是對數值輸出的格式進行微調,在宏定義中是作為要求輸出「#」後面的單變量的字符。例如定義如下宏定義:


#define PRINT
(x
) printf
(#x "= %d\n"
,x
)
  

當i=3,調用


PRINT
(i
);
  

時,將輸出


i= 3
  

除了+標誌和空格標誌之外,其餘的標誌符都是獨立的。

【例8.20】演示使用「#」標誌符的例子。


#include <stdio.h>
#define PRINT
(x
) printf
("<debug>"#x "= %d\n"
,x
)
int  main 
( 
)
{
    int i
;
    printf
("%#d %#o %#x %#X\n"
,100
,100
,100
,100
);
    printf
("%#o 0%o %#x %#X\n"
,0
,0
,0
,0
);
    printf
("%.0f %#.0f %g %#g\n"
,5.0
,5.0
,5.0
,5.0
);
    for
( i=1
;i<3
;i++
)
         PRINT
(i
);
    return 0
;
}
  

程序運行結果如下:


100 0144 0x64 0X64
0 00 0 0
5 5. 5 5.00000
<debug>i= 1
<debug>i= 2
  

4.「*」替換符

printf函數允許間接指定域寬和精度。「*」替換符用來替換域寬修飾符或精度修飾符中的任意一個,或者兩者都替換。在這種情況下,printf函數首先從輸出量表中取得將要使用的域寬或精度的實際數值,然後用這個數值來完成輸出任務。

注意有些編譯系統的%%不能使用%m.n%的形式。例如語句


printf
("%*%\n"
,12
);
  

的含義是右對齊輸出%號,即別把它替換為域寬12,向右移動11個空格後輸出%號。但有些編譯系統則把這個語句當做


printf
("%*%\n"
,12
);
  

語句執行,僅僅左對齊輸出一個%號。所以在使用時,一定要驗證一下所使用的編譯系統如何處理它們。

替代參數可以是多個,參數賦值的順序要按參數的固有順序給出,先給替換參數,然後是要打印的參數,替換參數也可以是表達式,詳見下面的例題。

【例8.21】演示使用「*」替換符的例子。


#include <stdio.h>
#include <string.h>
int  main 
( 
)
{
     const int k1=12
;
     int k2=8
,k3=2
,k4=7
,j
;
     double i
;
     char st="How are you
?"
;
     printf
("%.*s\n"
,strlen
(st
),st
);
     printf
("%*.*s\n"
,12
,8
,st
);
     printf
("%*.*s\n"
,12
,12
,st
);
     printf
("%*.*s\n"
,k1
,k2
,st
);
     printf
("%*%\n"
,12
);          //
本系統對%
號不起作用
     printf
("%*c\n"
,12
,'A'
);     
     printf
("%*d\n"
,12
,123
);     
     printf
("%-*d/%+*d\n"
,6
,0
,6
,0
);     
     printf
("%+*d\n"
,12
,123
);     
     for
( i=-1
;i<2
;i++
)
           printf
("% .*e %+.*e %.*e\n"
,k3
,i
,k3
,i
,k3
,i
);
     for
( j=-1
;j<2
;j++
)
           printf
("%+*d %*d\n"
,3
,j
,-3
,j
);
     printf
("%*.*s %*.*s\n"
,k2
,k4
,st
,k1
,k1
,st
);
     printf
("%+*d %*.*e\n"
,k1
,123
,k2
,k3
,(double
)k1
);     
     return 0
;
}
  

程序運行結果如下:


How are you
?
    How are
How are you
?
    How are
%
           A
         123
0    /    +0
        +123
-1.00e+000 -1.00e+000 -1.00e+000
 0.00e+000 +0.00e+000  0.00e+000
 1.00e+000 +1.00e+000  1.00e+000
 -1 -1
 +0 0
 +1 1
 How are How are you
?
         +123 1.20e+001