一、輸入和輸出流
- C 語(yǔ)言的標(biāo)準(zhǔn)輸入輸出函數(shù)都是獨(dú)立于設(shè)備的,不需要考慮如何在特定設(shè)備上傳輸數(shù)據(jù);C 語(yǔ)言的庫(kù)函數(shù)和操作系統(tǒng)會(huì)確保在特定設(shè)備上的操作完全正常。
- C 語(yǔ)言的每個(gè)輸入源和輸出目的地都稱(chēng)為流(stream);流和設(shè)備實(shí)體相互獨(dú)立。
- 程序使用的設(shè)備通常都有一個(gè)或多個(gè)相關(guān)的流;
- 磁盤(pán)驅(qū)動(dòng)器一般包含多個(gè)文件,流和數(shù)據(jù)源或目的地一一對(duì)應(yīng),而不是和設(shè)備一一對(duì)應(yīng)。
- 輸入輸出流還可以進(jìn)一步細(xì)分為:字符流(character stream)和二進(jìn)制流(binary stream)。
- 字符流中傳輸?shù)氖且幌盗凶址?,可以根?jù)格式規(guī)范庫(kù)例程修改;
- 二進(jìn)制流中傳輸?shù)臄?shù)據(jù)是以系列字節(jié),不能以任何方式修改。
二、標(biāo)準(zhǔn)流
流有標(biāo)識(shí)它的名稱(chēng)。C 語(yǔ)言有三個(gè)在 <stdio.h> 頭文件中預(yù)定義的標(biāo)準(zhǔn)流,程序只要包含這個(gè)頭文件,就可以使用這些流。這三個(gè)標(biāo)準(zhǔn)流是:
- stdin
- stdout
- stderr
stderr 流只是將來(lái)自 C 庫(kù)的錯(cuò)誤信息傳送出去,也可以將自己的錯(cuò)誤信息傳送給 sterr。寫(xiě)入 stdout 和 stderr 這兩個(gè)流的數(shù)據(jù)的目的地默認(rèn)為命令行,這兩個(gè)流的主要差別是:
- 輸出到 stdout 的流在內(nèi)存上緩存,所以寫(xiě)入到 stdout 的數(shù)據(jù)不會(huì)馬上送到設(shè)備。
- 輸出到 stderr 的流不進(jìn)行緩存,所以寫(xiě)入 stderr 的數(shù)據(jù)會(huì)立即傳送到設(shè)備上。
對(duì)于緩存的流,程序會(huì)在內(nèi)存中傳輸緩存區(qū)域的數(shù)據(jù),在物理設(shè)備上傳輸數(shù)據(jù)可以異步進(jìn)行。這使得輸入輸出操作更為高效。
使用操作系統(tǒng)命令,stdin 和 stdout 都可以重定向到文件上,而不是默認(rèn)的鍵盤(pán)和屏幕上。
三、鍵盤(pán)輸入
stdin 上的鍵盤(pán)輸入有兩種形式:
- 格式化輸入,主要由 scanf() 提供;
- 非格式化輸入,通過(guò) getchar() 等函數(shù)接收原始的字符串?dāng)?shù)據(jù)。
3.1、格式化鍵盤(pán)輸入
scanf 從 stdin 流中讀入字符,并根據(jù)格式控制字符串中的格式說(shuō)明符,將它們轉(zhuǎn)換成一個(gè)或多個(gè)值。
示例代碼:
char string[100];
scanf("%s",string);
3.2、輸入格式控制字符串
在 scanf() 函數(shù)中使用的格式控制字符串不完全類(lèi)似于 printf() 中的格式控制字符串。在格式控制字符串中添加一個(gè)或多個(gè)空白字符串,如空格 ' '、制表符 '\t' 或換行符 '\n',scanf() 會(huì)忽略空白字符,直接讀入輸入中的下一個(gè)非空白字符。在格式控制字符串中只要出現(xiàn)一個(gè)空白字符,就會(huì)忽略無(wú)數(shù)個(gè)連續(xù)的空白字符。要注意的是,scanf() 默認(rèn)忽略空白字符,但使用 %c、%[] 或 %n 說(shuō)明符讀取數(shù)據(jù)時(shí)除外(見(jiàn)下表)。
scanf() 會(huì)讀入任何非空白字符(除了%以外),但不會(huì)存儲(chǔ)這個(gè)連續(xù)出現(xiàn)的字符。以下是輸入格式符的一般形式:
% * field_width 長(zhǎng)度修飾符 conversion_character
含義如下:
| 格式部分 | 意 義 |
|---|---|
| % | 表示格式說(shuō)明符的開(kāi)始 |
| * | 表示要忽略的輸入值 |
| field_width | 指定輸入字段中的字符數(shù) |
| 長(zhǎng)度修飾符 | 指定數(shù)值和指針輸入值的長(zhǎng)度 |
| conversion_character | 指定要把輸入轉(zhuǎn)換成什么類(lèi)型 |
以下是一般格式的個(gè)部分說(shuō)明:
- %:表示格式說(shuō)明符的開(kāi)頭,不能省略。
- *:可選,表示忽略下一個(gè)輸入值。
- 字段寬度:可選,它是一個(gè)整數(shù),指定了 scanf() 讀入的字符數(shù)。
- 長(zhǎng)度修飾符:可選,
示例代碼:
#include <stdio.h>
#define SIZE 20
void try_input(char* prompt, char* format);
int main() {
try_input("Enter as input: -2.35 15 25 ready2go\n", "%f %d %d %[abcdefghijklmnopqrstuvwxyz] %*1d %s%n");
try_input("\nEnter the same input again: ", "%4f %4d %d %*d %[abcdefghijklmnopqrstuvwxyz] %*1d %[^o]%n");
try_input("Enter as input: -2.3A 15 25 ready2go\n", "%4f %4d %d %*d %[abcdefghijklmnopqrstuvwxyz] %*1d %[^o]%n");
return 0;
}
void try_input(char* prompt, char* format) {
int value_count = 0;
float fp1 = 0.0f;
int i = 0;
int j = 0;
char word1[SIZE] = " ";
char word2[SIZE] = " ";
int byte_count = 0;
printf(prompt);
value_count = scanf(format, &fp1, &i, &j, word1, word2, &byte_count);
fflush(stdin);
printf("The input format string for scanf() is:\n \"%s\"\n",format);
printf("Count of bytes read = %d\n",byte_count);
printf("Count of values read = %d\n",value_count);
printf("fp1 = %f i = %d j = %d\n",fp1, i, j);
printf("word1 = %s word2 = %s\n",word1, word2);
}
3.3、輸入格式字符串中的字符
可以在輸入格式字符串中包含一些不是格式轉(zhuǎn)換說(shuō)明符的字符。為此,必須指定輸入中有這些字符,且 scanf() 函數(shù)應(yīng)讀取它們,但不存儲(chǔ)它們。這些非格式轉(zhuǎn)換字符必須和輸入流的字符完全相同,只要有一個(gè)不同,scanf() 就會(huì)終止輸入。
示例代碼:
#include <stdio.h>
int main() {
int i = 0;
int j = 0;
int value_count = 0;
float fp1 = 0.0f;
printf("Enter: fp1 = 3.14159 i = 7 8\n");
printf("\nInput:");
value_count = scanf("fp1 = %f i = %d %d", &fp1, &i, &j);
printf("\nOutput:");
printf("Count of values read = %d\n", value_count);
printf("fp1 = %f\ti = %d\tj = %d\n", fp1, i, j);
return 0;
}
運(yùn)行結(jié)果:
Enter: fp1 = 3.14159 i = 7 8
輸入:
Input:fp1 = 3.14 i = 4 5
輸出:
Output:Count of values read = 3
fp1 = 3.140000 i = 4 j = 5
這些非格式轉(zhuǎn)換字符必須和輸入流的字符完全相同,才會(huì)得到正常的輸出結(jié)果。
3.4、輸入浮點(diǎn)數(shù)的各種變化
使用 scanf() 函數(shù)讀取格式化的浮點(diǎn)數(shù)時(shí),不僅可以選擇格式說(shuō)明符,還可以輸入不同形式的數(shù),示例代碼:
#include <stdio.h>
int main() {
float fp1 = 0.0f;
float fp2 = 0.0f;
float fp3 = 0.0f;
int value_count = 0;
printf("Enter: 3.14.314E1.0314e+02\n");
printf("\nInput:");
value_count = scanf("%f %f %f", &fp1, &fp2, &fp3);
printf("\nOutput:");
printf("Number of values read = %d\n", value_count);
printf("fp1 = %f fp2 = %f fp3 = %f\n", fp1, fp2, fp3);
return 0;
}
執(zhí)行結(jié)果:
Enter: 3.14.314E1.0314e+02
Input:3.14.314E1.0314e+02
Output:Number of values read = 3
fp1 = 3.140000 fp2 = 3.140000 fp3 = 3.140000
3.5、讀取十六進(jìn)制和八進(jìn)制值
可以使用格式說(shuō)明符 %x 從輸入流中讀取十六進(jìn)制值,使用 %o 讀取八進(jìn)制值,示例代碼如下:
#include <stdio.h>
int main() {
int i = 0;
int j = 0;
int k = 0;
int n = 0;
printf("Enter three integer values\n");
n = scanf("%d %x %o", &i, &j, &k);
printf("\nOutput:");
printf("%d values read.\n", n);
printf("i = %d j = %d k = %d\n", i, j, k);
return 0;
}
執(zhí)行結(jié)果:
Enter three integer values: 12 12 12
Output:3 values read.
i = 12 j = 18 k = 10
3.6、使用 scanf() 讀取字符
使用 %c 可以讀取一個(gè)字符,并將它存儲(chǔ)為 char 類(lèi)型。對(duì)于字符串可以使用 %s 或是 %[]。此時(shí)要給存儲(chǔ)的字符串追加上 '\0',作為最后一個(gè)字符。要注意的是,%[] 可以讀入含有空格的字符串,
但是 %s 不可以。使用 %[] 說(shuō)明符時(shí),只需要在方括號(hào)內(nèi)包含空格字符即可。
示例代碼:
#include <stdio.h>
#define MAX_TOWN 10
int main() {
char initial = ' ';
char name[80] = {' '};
char age[4] = {'0'};
printf("Enter your first initial: ");
scanf("%c", &initial);
printf("Enter your first name: ");
scanf("%s", name);
fflush(stdin);
if(initial != name[0]) printf("%s, you got your initial wrong.\n", name);
else printf("Hi, %s. Your initial is correct. Well done!\n", name);
printf("Enter your full name and your age separated by a comma:\n");
scanf("%[^,] , %[0123456789]", name, age);
printf("\nYour name is %s and your age are %s years old.\n", name, age);
return 0;
}
執(zhí)行結(jié)果:
Enter your first initial: I
Enter your first name: Ivor
Hi, Ivor. Your initial is correct. Well done!
Enter your full name and your age separated by a comma:
Ivor Horton , 23
Your name is
Ivor Horton and your age are 23 years old.
scanf("%c", &initial) 中如果輸入空格,那么程序就會(huì)將空白字符當(dāng)成 initial 的值,根據(jù)控制字符串的定義方式,使用說(shuō)明符 %c 輸入的第一個(gè)字符就是要提取的字符。如果不接受空格
為 initial,可以修改為以下輸入語(yǔ)句:
scanf(" %c", &initial);
控制字符串中的第一個(gè)字符是空格,因此 scanf() 會(huì)讀入且忽略所有空格。
scanf("%[^,] , %[0123456789]", name, age) 中“,”后的空格也是必需的,原因與以上語(yǔ)句一樣。
3.7、從鍵盤(pán)上輸入字符串
<stdio.h> 中的 gets_s() 函數(shù)可以將一整行的文本作為字符串讀入。其函數(shù)原型如下:
char *gets_s(char *str, rsize_t n);
該函數(shù)會(huì)將之多 n-1 個(gè)連續(xù)字符讀入指針 str 所指向的內(nèi)存中,直到按下回車(chē)為止。它會(huì)用中止字符‘\0’取代按下的回車(chē)鍵時(shí)讀入的換行符。其返回值與第一個(gè)變?cè)嗤?。如果在輸入?br> 過(guò)程中出錯(cuò),str[0] 就設(shè)置為‘\0’字符??梢允褂脴?biāo)準(zhǔn)庫(kù)函數(shù) fgets() 替代該函數(shù),示例代碼如下:
#include <stdio.h>
#define MAX_TOWN 10
int main() {
char initial[3] = {' '};
char name[80] = {' '};
printf("Enter your first initial: ");
fgets(initial, sizeof(initial), stdin);
printf("Enter your name: ");
fgets(name, sizeof(name), stdin);
if(initial[0] != name[0]) printf("%s, you got your initial wrong.\n", name);
else printf("Hi, %s, Your initial is correct. Well done!\n", name);
return 0;
}
執(zhí)行結(jié)果:
Enter your first initial: I
Enter your name: Ivor Normal
Hi, Ivor Normal
Your initial is correct. Well done!
fgets() 函數(shù)讀取的字符數(shù)比第二個(gè)變?cè)付ǖ淖址麛?shù)少 1,再添加終止字符'\0'。fgets() 函數(shù)再輸入字符串中存儲(chǔ)一個(gè)換行符來(lái)對(duì)應(yīng)按下的回車(chē)鍵,而 gets_s() 函數(shù)不是這樣。
為了避免調(diào)用 printf() 輸出姓名時(shí)輸出一個(gè)換行符,需要覆蓋這個(gè)換行符。
對(duì)于字符串輸入,使用 gets_s() 和 fgets() 通常是首選,除非要控制字符串的內(nèi)容,此時(shí)可以使用 %[]。
3.8、單個(gè)字符的鍵盤(pán)輸入
stdio.h 中的 getc() 函數(shù)從流中讀取一個(gè)字符,把字符代碼返回為 int 類(lèi)型。getc() 的變?cè)橇鳂?biāo)識(shí)的。讀到流的末尾時(shí),getc() 返回 EOF,這個(gè)符號(hào)在 stdio.h 中總是定義
為一個(gè)負(fù)整數(shù)。因?yàn)?char 類(lèi)型可以由編譯器的開(kāi)發(fā)人員確定為帶符號(hào)的或是不帶符號(hào)的,所以 getc() 不返回 char 類(lèi)型。如果它返回 char 類(lèi)型的值,則 char 定義為無(wú)符號(hào)的類(lèi)型
時(shí),就不能返回 EOF。一般情況下,函數(shù)返回 int 類(lèi)型的值,但是希望返回 char 類(lèi)型時(shí),幾乎總是可以肯定它需要返回 EOF。
getchar() 函數(shù)可以從 stdin 中一次讀取一個(gè)字符,等價(jià)于用 stdin 變?cè){(diào)用 getc()。getchar() 在 stdio.h 中定義,原型如下:
int getchar(void);
getchar() 不需要變?cè)?,它?huì)把輸入流中讀入的字符代碼返回為 int 類(lèi)型。getchar() 和 putchar() 每次只處理一個(gè)字符,示例代碼:
#include <stdio.h>
int main(void) {
char ch;
while((ch = getchar()) != '#') {
putchar(ch);
}
return 0;
}
執(zhí)行結(jié)果:
Hello MARVEL#I'm Iron man
Hello MARVEL
這里涉及了緩沖區(qū),用戶(hù)輸入的字符被收集并存儲(chǔ)在與一個(gè)被稱(chēng)為緩沖區(qū)的臨時(shí)存儲(chǔ)區(qū),按下 ENTER 鍵后,程序才可使用用戶(hù)輸入的字符。緩沖區(qū)的意義是:首先,把若干字符作為一個(gè)塊進(jìn)行傳輸比逐個(gè)
發(fā)送這些字符節(jié)省時(shí)間;其次,如果用戶(hù)打錯(cuò)了字符,可以直接通過(guò)鍵盤(pán)修正錯(cuò)誤。
緩沖分為兩類(lèi):
- 完全緩沖 I/O:緩沖區(qū)被填滿(mǎn)時(shí)才刷新緩沖區(qū),通常出現(xiàn)在文件輸入流中;
- 行緩沖 I/O:出現(xiàn)換行符時(shí)刷新緩沖區(qū),鍵盤(pán)輸入通常是行緩沖輸入。
ANSI C 和 后續(xù)的 C 標(biāo)準(zhǔn)都規(guī)定輸入是緩沖的,不過(guò)最初 K&R 把這個(gè)權(quán)利交給了編譯器的編寫(xiě)者。
stdio.h 中也聲明了 ungetc() 函數(shù),它允許把剛才讀取的一個(gè)字符放回輸入流。該函數(shù)需要兩個(gè)參數(shù):輸入流
的字符和流的標(biāo)識(shí)符(對(duì)于標(biāo)準(zhǔn)流就是 stdin)。ungetc() 返回一個(gè) int 類(lèi)型的值,對(duì)應(yīng)放回輸入流的字符,如果操作失敗,返回一個(gè)特殊的字符 EOF。
原則上,可以把一連串字符串放回輸入流,但只保證一個(gè)字符有效。
示例代碼如下:
#include <stdio.h>
#include <ctype.h>
#include <stdbool.h>
#include <string.h>
#define LENGTH 50
void eatspace(void);
bool getinteger(int* n);
char* getname(char* name, size_t length);
bool isnewline(void);
int main(void) {
int number;
char name[LENGTH] = {'\0'};
printf("Enter a sequence of integers and alphabetic names in asingle line:\n");
while(!isnewline()) {
if(getinteger(&number)) printf("Integer value: %8d\n", number);
else if(strlen(getname(name,LENGTH)) > 0) printf("Name: %s\n", name);
else {
printf("Invalid input.\n");
return 1;
}
}
return 0;
}
void eatspace(void) {
char ch = 0;
while(isspace(ch = (char)getchar()));
ungetc(ch, stdin);
}
bool getinteger(int* n) {
eatspace();
int value = 0;
int sign = 1;
char ch = 0;
if((ch = (char)getchar()) == '-') sign = -1;
else if(isdigit(ch)) value = ch - '0';
else if(ch != '+') {
ungetc(ch, stdin);
return false;
}
while(isdigit(ch = (char)getchar())) {
value = 10*value + (ch - '0');
}
ungetc(ch,stdin);
*n = value * sign;
return true;
}
char* getname(char* name, size_t length) {
eatspace();
size_t count = 0;
char ch = 0;
while(isalpha(ch = (char)getchar())) {
name[count++] = ch;
if(count == length -1) break;
}
name[count] = '\0';
if(count < length - 1) ungetc(ch, stdin);
return name;
}
bool isnewline(void) {
char ch = 0;
if((ch = (char)getchar()) == '\n') return true;
ungetc(ch, stdin);
return false;
}
執(zhí)行結(jié)果:
Enter a sequence of integers and alphabetic names in asingle line:
Ivor 78 Horton 34 Jane 18
Name: Ivor
Integer value: 78
Name: Horton
Integer value: 34
Name: Jane
Integer value: 18
四、屏幕輸出
將格式化數(shù)據(jù)傳輸?shù)?stout 流的主要函數(shù)是 printf(),stdio.h 中還聲明了可選的 printf_s() 函數(shù),也就是 printf() 函數(shù)的安全版本。printf_s() 和 printf() 的
主要區(qū)別是 printf_s() 不允許在格式字符串中包含 %n,這是因?yàn)?該輸出說(shuō)明符會(huì)把數(shù)據(jù)寫(xiě)入內(nèi)存,導(dǎo)致不安全。
4.1、使用 printf() 的格式化輸出
函數(shù)原型如下:
int printf(char *format, ...);
printf() 函數(shù)的指針變?cè)荒転?NULL,如果變?cè)嘤喔袷秸f(shuō)明符,就會(huì)忽略多余的變?cè)?/p>
printf() 的格式轉(zhuǎn)換說(shuō)明符比 scanf() 復(fù)雜很多,輸出格式說(shuō)明符一般形式如下:
% flag field_width precision size_flag conversion_character
其內(nèi)容和意義如下:
| 格式部分 | 標(biāo) 記 | 意 義 | 是否可選 |
|---|---|---|---|
| % | 格式說(shuō)明符的開(kāi)頭 | 否 | |
| flag | -, +, space, # or 0 | 影響輸出的標(biāo)記 | 是 |
| field_width | 輸出字段中的最小字符數(shù) | 是 | |
| precision | 精度指定符 | 是 | |
| size_flag | h, hh, l, ll, j, z, t, t or L | 修改轉(zhuǎn)換類(lèi)型的尺寸標(biāo)記 | 是 |
| conversion_character | d, i, o, u, x, X, p, n, e, E, f, F, g, G, a, A, c, or s | 要使用的輸出轉(zhuǎn)換類(lèi)型 | 否 |
影響輸出的標(biāo)記:
| 字 符 | 用 途 |
|---|---|
| + | 對(duì)于有符號(hào)的輸出,這個(gè)字符確保輸出值的前面總是有一個(gè)符號(hào)+或-。默認(rèn)情況下,只有符號(hào)有 - 號(hào) |
| - | 指定輸出值在輸出字段中左對(duì)齊,右邊用空格填充。輸出的默認(rèn)對(duì)齊方式為右對(duì)齊 |
| 0 | 指定在輸出值的前面填充0,以填滿(mǎn)字段寬度 |
| # | 指定將0放在八進(jìn)制的輸出前面,將0X或0x放在十六進(jìn)制的輸出的前面,或者浮點(diǎn)數(shù)包含小數(shù)點(diǎn)。對(duì)于g或G浮點(diǎn)轉(zhuǎn)換字符,忽略尾部的0 |
| space | 指定在正數(shù)或0輸出值前面放置一個(gè)空格,而不是+號(hào) |
輸出字段中的最小字符數(shù):
如果輸出需要更多的字符,它會(huì)自動(dòng)增加。如果輸出值需要的字符小于指定的最小字符數(shù),多余位置會(huì)填充空白,除非字段寬度用前導(dǎo) 0 指定,例如 09,此時(shí)在左邊會(huì)補(bǔ)入 0。
精度指定符:
它通常用于浮點(diǎn)數(shù)的輸出,包含一個(gè)小數(shù)點(diǎn)后跟一個(gè)整數(shù)。說(shuō)明符 .n 表示輸出值精確到小數(shù)點(diǎn)后 n 位。如果輸出的小數(shù)點(diǎn)位數(shù)多于 n,就四舍五入或舍棄掉。如果把它用于整數(shù)裝換,它就指定要在
輸出中顯式的最少位數(shù)。
尺寸標(biāo)記:
| 標(biāo) 記 | 作 用 |
|---|---|
| h | 其后的整數(shù)轉(zhuǎn)換說(shuō)明符應(yīng)用于 short 或 unsigned short 變?cè)?/td> |
| hh | 其后的整數(shù)轉(zhuǎn)換說(shuō)明符應(yīng)用于 signed char 或 unsigned char 變?cè)?/td> |
| l | 其后的整數(shù)轉(zhuǎn)換說(shuō)明符應(yīng)用于 long 或 unsigned long 變?cè)?/td> |
| ll | 其后的整數(shù)轉(zhuǎn)換說(shuō)明符應(yīng)用于 long long 或 unsigned long long 變?cè)?/td> |
| j | 其后的整數(shù)轉(zhuǎn)換說(shuō)明符應(yīng)用于 intmax 或 uintmax_t 變?cè)T摌?biāo)記會(huì)避免編譯器發(fā)出警告,因?yàn)?size_t 取決于是實(shí)現(xiàn)碼的整數(shù)類(lèi)型 |
| z | 其后的整數(shù)轉(zhuǎn)換說(shuō)明符應(yīng)用于 size_t 變?cè)?/td> |
| t | 其后的整數(shù)轉(zhuǎn)換說(shuō)明符應(yīng)用于 ptrdiff_t 變?cè)?/td> |
| L | 其后的浮點(diǎn)數(shù)轉(zhuǎn)換說(shuō)明符應(yīng)用于 long double 變?cè)?/td> |
轉(zhuǎn)換字符:
| 應(yīng)用類(lèi)型 | 轉(zhuǎn)換字符 | 生成的輸出 |
|---|---|---|
| 整數(shù) | ||
| d 或 i | 帶符號(hào)的十進(jìn)制整數(shù)值 | |
| o | 不帶符號(hào)的八進(jìn)制整數(shù)值 | |
| u | 不帶符號(hào)的十進(jìn)制整數(shù)值 | |
| x | 不帶符號(hào)的十六進(jìn)制整數(shù)(使用小寫(xiě)的十六進(jìn)制數(shù)) | |
| X | 不帶符號(hào)的十六進(jìn)制整數(shù)(使用大寫(xiě)的十六進(jìn)制數(shù)) | |
| 浮點(diǎn)數(shù) | ||
| f 或 F | 帶符號(hào)的小數(shù)值 | |
| e | 帶符號(hào)和指數(shù)的小數(shù)值 | |
| E | 與 e 相同,使用 E | |
| g | 與 e 或 f 相同,取決于值的大小和精度 | |
| G | 與 g 相同,但用 E 表示指數(shù) | |
| A 或 a | 用十六進(jìn)制表示雙精度值,十六進(jìn)制的尾數(shù)前面加上 0x 或 0X 前綴,指數(shù)前加上 p 或 P,例如:0xh.hhhp±d | |
| 指針 | ||
| p | 把變?cè)妮敵鰹橹羔槪冊(cè)念?lèi)型應(yīng)該是 void* | |
| 字符 | ||
| c | 單個(gè)字符或精度字符 | |
| s | 在‘\0’之前的所有字符串或已輸出的 precision 個(gè)字符 |
注意:%n 只能用于 printf()。對(duì)應(yīng)的變?cè)仨毷?int* 類(lèi)型。作用是將字符數(shù)輸入 stdout。
4.2、轉(zhuǎn)移序列
| 轉(zhuǎn)移序列 | 說(shuō) 明 |
|---|---|
| \b | 退格 |
| \f | 換頁(yè) |
| \n | 換行 |
| \r | 回車(chē)(用于打印機(jī)),在屏幕輸出中,就是移動(dòng)到當(dāng)前行的開(kāi)頭 |
| \t | 水平制表符 |
4.3、整數(shù)輸出
示例代碼:
#include <stdio.h>
int main() {
int i = 15, j = 345, k = 4567;
long long li = 56789LL, lj = 67891234567LL, lk = 23456789LL;
printf("i = %d j = %d k = %d i = %6.3d j = %6.3d k = %6.3d\n", i, j, k, i, j, k);
printf("i = %-d j = %+d k = %-d i = %-6.3d j = %-6.3d k = %-6.3d\n", i, j, k, i, j, k);
printf("li = %d lj = %d lk = %d\n", li, lj, lk);
printf("li = %lld lj = %lld lk = %lld\n", li, lj, lk);
return 0;
}
編譯時(shí)會(huì)有如下警告:
warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long long int’ [-Wformat=]
printf("li = %d lj = %d lk = %d\n", li, lj, lk);
執(zhí)行結(jié)果:
i = 15 j = 345 k = 4567 i = 015 j = 345 k = 4567
i = 15 j = +345 k = 4567 i = 015 j = 345 k = 4567
li = 56789 lj = -828242169 lk = 23456789
li = 56789 lj = 67891234567 lk = 23456789
由以上輸出結(jié)果可以看出,標(biāo)志“-”使輸出左對(duì)齊。對(duì)于第二個(gè) i 值的輸出,插入了一個(gè)前導(dǎo) 0, 因?yàn)樽钚【戎付?3.如果在格式說(shuō)明符的最小字符寬度前放置一個(gè) 0,并忽略精度說(shuō)明符,也可以
得到相同的結(jié)果。有精度說(shuō)明符時(shí),會(huì)忽略前導(dǎo) 0。
第三行輸出在編譯時(shí)會(huì)發(fā)出警告,給出值的字符寬度及精度不夠大,會(huì)造成意想不到的結(jié)果。
4.4、輸出浮點(diǎn)數(shù)
示例代碼:
#include <stdio.h>
int main() {
float fp1 = 345.678f, fp2 = 1.234E6f;
double fp3 = 234567898.0, fp4 = 11.22334455e-6;
printf("%f %+f %-10.4f %6.4f\n", fp1, fp2, fp1, fp2);
printf("%e %+E\n", fp1, fp2);
printf("%f %g %#+f %8.4f %10.4g\n", fp3, fp3, fp3, fp3, fp4);
return 0;
}
執(zhí)行結(jié)果:
345.678009 +1234000.000000 345.6780 1234000.0000
3.456780e+02 +1.234000E+06
234567898.000000 2.34568e+08 +234567898.000000 234567898.0000 1.122e-05
fp1 的第二個(gè)輸出值說(shuō)明熱如何限制小數(shù)點(diǎn)后的位數(shù)。為 fp2 的第二個(gè)輸出指定的字符寬度太小,放不下小數(shù)位,因此會(huì)舍棄多余的部分。
4.5、字符輸出
示例代碼:
#include <stdio.h>
#include <limits.h>
#include <ctype.h>
int main() {
int count = 0;
printf("The printable characters are the following:\n");
for(int code = 0; code <= CHAR_MAX; ++code) {
char ch = (char)code;
if(isprint(ch)) {
if(count++ % 32 == 0) printf("\n");
printf(" %c", ch);
}
}
printf("\n");
return 0;
}
執(zhí)行結(jié)果:
! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
@ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _
` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~
五、其他輸出函數(shù)
在 stdio.h 頭文件中聲明的 puts() 函數(shù)和 gets_s() 函數(shù)互補(bǔ)。該函數(shù)原型如下:
int puts(const char *string);
puts() 函數(shù)接受字符串指針作為變?cè)?,將字符串后的一個(gè)換行符寫(xiě)入標(biāo)準(zhǔn)輸出流 stdout。其字符串必須用字符‘\0’終止。puts() 函數(shù)的參數(shù)是 const,所以該函數(shù)不能修改
傳送給她的字符串。puts() 函數(shù)用于輸出單行信息,例如:
puts("Is there no end to input and output?");
使用 printf() 函數(shù)必須在字符串末尾添加‘\n’,才能達(dá)到該效果。
5.1 屏幕的非格式化輸出
函數(shù) putchar() 也是 stdio.h 中定義的函數(shù),與 getchar() 函數(shù)互補(bǔ)。其原型如下:
int putchar(int c);
該函數(shù)將單個(gè)字符 c 輸出到 stdout 上,并返回所顯示的字符。它可以輸出信息,一次先是一個(gè)字符,該方法可以控制是否輸出某些字符。例如,輸出一個(gè)字符串:
char string[] = "Beware the Jabberwock, \nmy son!";
puts(string);
或是:
int i = 0;
while() {
if(string[i] != '\n') putchar(string[i]);
++i;
}
5.2、數(shù)組的格式化
在 stdio.h 中聲明的 sprintf_s() 或 snprintf_s() 函數(shù),可以格式化數(shù)據(jù)寫(xiě)入 char 類(lèi)型的數(shù)組中,它們是 sprintf() 標(biāo)準(zhǔn)函數(shù)的安全版本,因此它們禁止寫(xiě)到數(shù)組外部。它們的區(qū)別是,
sprintf_s() 將超出數(shù)組的范圍看作一個(gè)運(yùn)行錯(cuò)誤,而 snprintf_s() 僅截?cái)噍敵?,使結(jié)果能放置在數(shù)組中。這里僅討論 snprintf_s(),其函數(shù)原型如下:
int snprintf_s(char * restrict str, rsize_t n, const * restrict format, ...);
第一個(gè)變?cè)菙?shù)組的地址,是數(shù)組的地址,使輸出的目的地。第二個(gè)變?cè)迨遄娴麻L(zhǎng)度,它的工作方式與 printf_s() 相同,只是將數(shù)據(jù)寫(xiě)入 str,而不是 stdout。示例代碼:
char result[20];
int count = 4;
int nchars = snprintf_s(result, sizeof(result), "A dog has %d legs.", count);
5.3、數(shù)組的格式化輸入
可選的 sscanf_s() 函數(shù)與 snprintf_s() 函數(shù)互補(bǔ),因?yàn)?sscanf_s() 函數(shù)可以在格式字符串的控制下,從 char 類(lèi)型的數(shù)組元素中讀取數(shù)據(jù)。 它是 sscanf() 函數(shù)的安全版本,主要區(qū)別是
sscanf_s() 需要給每個(gè) c、s 或是 [ 說(shuō)明符指定地址和長(zhǎng)度變?cè)?sscanf() 只需要地址。sscanf_s() 函數(shù)的原型如下:
int sscanf_s(const char * restrict str, const char * restrict format, ...);
示例代碼:
char *source = "Fred 94";
char name[10] ={0};
int age = 0;
int items = sscanf_s(source, " %s %d", name, sizeof(name), &age);
以上代碼的執(zhí)行結(jié)果是:name 包含字符串 "Fred",age 的值是 94。items 變量的值是 2,因?yàn)閺膕ource 中讀取了兩項(xiàng)。