文章目錄
一、輸出
名稱 | 用法 |
---|---|
putchar() | 只能輸出單個字符 |
puts() | 只能輸出字符串,並且輸出結束後會自動換行 |
printf() | 可以輸出各種類型的數據 |
對於printf()函數的格式控制符完整形式如下:
%[flag][width][.precision]type
1) type
tpye表示輸出類型,比如 %d、%f、%c、%lf,type 就分別對應 d、f、c、lf;再如,%-9d中 type 對應 d。
type 這一項必須有,這意味着輸出時必須要知道是什麼類型。
彙總一下常見的格式控制符:
格式控制符 | 說明 |
---|---|
%c | 輸出一個單一的字符 |
%hd、%d、%ld | 以十進制、有符號的形式輸出 short、int、long 類型的整數 |
%hu、%u、%lu | 以十進制、無符號的形式輸出 short、int、long 類型的整數 |
%ho、%o、%lo | 以八進制、不帶前綴、無符號的形式輸出 short、int、long 類型的整數 |
%#ho、%#o、%#lo | 以八進制、帶前綴、無符號的形式輸出 short、int、long 類型的整數 |
%hx、%x、%lx;%hX、%X、%lX | 以十六進制、不帶前綴、無符號的形式輸出 short、int、long 類型的整數。如果 x 小寫,那麼輸出的十六進制數字也小寫;如果 X 大寫,那麼輸出的十六進制數字也大寫。 |
%#hx、%#x、%#lx;%#hX、%#X、%#lX | 以十六進制、帶前綴、無符號的形式輸出 short、int、long 類型的整數。如果 x 小寫,那麼輸出的十六進制數字和前綴都小寫;如果 X 大寫,那麼輸出的十六進制數字和前綴都大寫。 |
%f、%lf | 以十進制的形式輸出 float、double 類型的小數 |
%e、%le;%E、%lE | 以指數的形式輸出 float、double 類型的小數。如果 e 小寫,那麼輸出結果中的 e 也小寫;如果 E 大寫,那麼輸出結果中的 E 也大寫。 |
%g、%lg;%G、%lG | 以十進制和指數中較短的形式輸出 float、double 類型的小數,並且小數部分的最後不會添加多餘的 0。如果 g 小寫,那麼當以指數形式輸出時 e 也小寫;如果 G 大寫,那麼當以指數形式輸出時 E 也大寫。 |
%s | 輸出一個字符 |
%p、%P | 以十六進制的形式輸出數據的地址。P的大小寫決定了十六進制前綴的大小寫形式。(這裏的地址都是虛擬地址,不等於數據在內存中的物理地址) |
注意:
- 一個數字,是由默認類型的:對於整數,默認是int類型;對於小數,默認是double類型;
- %g默認醉倒保留六位有效數字,包括整數和小數部分;%f和%e默認保留六位小數,只包括小數部分;
- %g不會在最後強加0來湊夠有效數字的位數,但是%f和%e會在最後強加0來湊夠小數部分的位數;
- 從上表中能看出,能夠以有符號形式輸出的只有十進制的整數
2) width
width表示最小輸出寬度,也就是至少佔用幾個字符的位置;
當輸出結果的寬度不足 width 時,以空格補齊(如果沒有指定對齊方式,默認會在左邊補齊空格);當輸出結果的寬度超過 width 時,width 不再起作用,按照數據本身的寬度來輸出。
3) .precision
.precision 表示輸出精度,也就是小數的位數
- 當小數部分的位數大於 precision 時,會按照四捨五入的原則丟掉多餘的數字;
- 當小數部分的位數小於 precision 時,會在後面補 0。
另外,.precision 也可以用於整數和字符串,但是功能卻是相反的:
- 用於整數時,.precision 表示最小輸出寬度。與 width 不同的是,整數的寬度不足時會在左邊補 0,而不是補空格。
- 用於字符串時,.precision 表示最大輸出寬度,或者說截取字符串。當字符串的長度大於 precision 時,會截掉多餘的字符;當字符串的長度小於 precision 時,.precision 就不再起作用。
4) flag
flag 是標誌字符。例如,%#x中 flag 對應 #,%-9d中 flags 對應-。下表列出了 printf() 可以用的 flag:
標誌字符 | 含義 |
---|---|
- | -表示左對齊。如果沒有,就按照默認的對齊方式,默認一般爲右對齊 |
+ | 用於整數或者小數,表示輸出符號(正負號)。如果沒有,那麼只有負數纔會輸出符號 |
空格 | 用於整數或者小數,輸出值爲正時冠以空格,爲負時冠以負號 |
# | 對於八進制(%o)和十六進制(%x / %X)整數,# 表示在輸出時添加前綴;八進制的前綴是 0,十六進制的前綴是 0x / 0X。對於小數(%f / %e / %g),# 表示強迫輸出小數點。如果沒有小數部分,默認是不輸出小數點的,加上 # 以後,即使沒有小數部分也會帶上小數點 |
二、輸入
1、輸入函數
名稱 | 用法 |
---|---|
getchar()、getche()、getch() | 這三個函數都用於輸入單個字符 |
gets() | 獲取一行數據,並作爲字符串處理 |
scanf() | 可以輸入多種類型的數據 |
2、getchar()、getche()、getch()區別
函數 | 作用 | 緩衝區 | 頭文件 | 回顯 | 適用平臺 |
---|---|---|---|---|---|
getchar() | 獲取一個字符(需要回車) | 有 | stdio.h | 有 | Windows、Linux、Mac OS 等所有平臺 |
getche() | 獲取一個字符(無需回車) | 無 | conio.h | 有 | Windows |
getch() | 獲取一個字符(無需回車) | 無 | conio.h | 無 | Windows |
3、gets與scanf區別
get函數:獲取字符串。gets() 是有緩衝區的,每次按下回車鍵,就代表當前輸入結束了。
gets() 和 scanf() 的主要區別是:
- scanf() 讀取字符串時以空格爲分隔,遇到空格就認爲當前字符串結束了,所以無法讀取含有空格的字符串。
- gets() 認爲空格也是字符串的一部分,只有遇到回車鍵時才認爲字符串輸入結束,所以,不管輸入了多少個空格,只要不按下回車鍵,對 gets() 來說就是一個完整的字符串。
故對於輸入一串字符串,若字符串中包含有空格,則需要使用gets()函數才能正確輸入。
4、scanf函數
scanf函數,格式控制符與printf函數相差無幾。只是scanf沒有了%p地址輸出控制符號。
&稱爲取地址符,也就是獲取變量在內存中的地址。
scanf() 控制字符串的完整寫法爲:
%{*} {width} type
其中,{ } 表示可有可無。各個部分的具體含義是:
- type表示讀取什麼類型的數據,例如 %d、%s、%[a-z]、%[^\n] 等;type 必須有。
- width表示最大讀取寬度,可有可無。
- *表示丟棄讀取到的數據,可有可無。
重點:
匹配特定字符 %[xxx]
使用連字符 -
常用的連字符舉例:
- %[a-z]表示讀取 abc…xyz 範圍內的字符,也即小寫字母;
- %[A-Z]表示讀取 ABC…XYZ 範圍內的字符,也即大寫字母;
- %[0-9]表示讀取 012…789 範圍內的字符,也即十進制數字。
你也可以將它們合併起來,例如:
- %[a-zA-Z]表示讀取大寫字母和小寫字母,也即所有英文字母;
- %[a-z-A-Z0-9]表示讀取所有的英文字母和十進制數字;
- %[0-9a-f]表示讀取十六進制數字。
不匹配某些字符
scanf() 允許我們在%[ ]中直接指定某些不能匹配的字符,具體方法就是在不匹配的字符前面加上^。例如:
- %[^\n]表示匹配除換行符以外的所有字符,遇到換行符就停止讀取;
- %[^0-9]表示匹配除十進制數字以外的所有字符,遇到十進制數字就停止讀取。
使用 scanf() 清空緩衝區的方案,就是:
scanf("%*[^\n]"); scanf("%*c");
下面我們就來解釋一下。
首先需要明白的是,等到需要清空緩衝區的時候,緩衝區中的最後一個字符一定是換行符\n,因爲輸入緩衝區是行緩衝模式,用戶按下回車鍵會產生換行符,結束本次輸入,然後輸入函數開始讀取。
scanf("%*[^\n]");將換行符前面的所有字符清空,scanf("%*c");將最後剩下的換行符清空。
Scanf函數從緩衝區讀取數據時的一些特性
當遇到 scanf() 函數時,程序會先檢查輸入緩衝區中是否有數據:
- 如果沒有,就等待用戶輸入。用戶從鍵盤輸入的每個字符都會暫時保存到緩衝區,直到按下回車鍵,產生換行符\n,輸入結束,scanf() 再從緩衝區中讀取數據,賦值給變量。
- 如果有數據,那就看是否符合控制字符串的規則:
- 如果能夠匹配整個控制字符串,那最好了,直接從緩衝區中讀取就可以了,就不用等待用戶輸入了。
- 如果緩衝區中剩餘的所有數據只能匹配前半部分控制字符串,那就等待用戶輸入剩下的數據。
- 如果不符合,scanf() 還會嘗試忽略一些空白符,例如空格、製表符、換行符等:
- 如果這種嘗試成功(可以忽略一些空白符),那麼再重複以上的匹配過程。
- 如果這種嘗試失敗(不能忽略空白符),那麼只有一種結果,就是讀取失敗。
注意:
- 換行符也是一個字符,也會進入緩衝區。
- scanf()、gets() 在讀取字符串時會忽略換行符,不會把換行符作爲字符串的內容。
- 空白符在大部分情況下都可以忽略。但是當控制字符串不是以格式控制符 %d、%c、%f 等開頭時,空白符就不能忽略了,它會參與匹配過程,如果匹配失敗,就意味着 scanf() 讀取失敗了。(這個結論是實踐得出的結論,沒有資料可以查詢)
三、關於緩衝區的一些總結
1、一些概念
緩衝區(Buffer)又稱爲緩存(Cache),是內存空間的一部分。緩衝區是爲了讓低速的輸入輸出設備和高速的用戶程序能夠協調工作,並降低輸入輸出設備的讀寫次數。
根據不同的標準,緩衝區可以有不同的分類。
-
根據緩衝區對應的是輸入設備還是輸出設備,可以分爲輸入緩衝區和輸出緩衝區。
-
根據數據刷新(也可以稱爲清空緩衝區,就是將緩衝區中的數據“倒出”)的時機,可以分爲全緩衝、行緩衝、不帶緩衝。
1)全緩衝
在這種情況下,當緩衝區被填滿以後才進行真正的輸入輸出操作。緩衝區的大小都有限制的,比如 1KB、4MB 等,數據量達到最大值時就清空緩衝區。
在實際開發中,將數據寫入文件後,打開文件並不能立即看到內容,只有清空緩衝區,或者關閉文件,或者關閉程序後,才能在文件中看到內容。這種現象,就是緩衝區在作怪。
2)行緩衝
在這種情況下,當在輸入或者輸出的過程中遇到換行符時,才執行真正的輸入輸出操作。行緩衝的典型代表就是標準輸入設備(也即鍵盤)和標準輸出設備(也即顯示器)。
Linux和macOS系統中printf和scanf函數都具有行緩衝區。
3)不帶緩衝
不帶緩衝區,數據就沒有地方緩存,必須立即進行輸入輸出。
getche()、getch() 就不帶緩衝區,輸入一個字符後立即就執行了,根本不用按下回車鍵。
Windows 下的 printf() 也不帶緩衝區,不管最後有沒有換行符\n,都會立即輸出,所以對於類似的輸出代碼,Windows 和 Linux、Mac OS 會有不同的表現。
錯誤信息輸出函數 perror() 也沒有緩衝區。錯誤信息必須刻不容緩、立即、馬上顯示出來,緩衝區將會增加捕獲錯誤的時間,這是毫無理由的。
總結
- 輸入:scanf()、getchar()、gets()都具有行緩衝,Windows 下特有的 getche() 和 getch(),不帶緩衝區;
- 輸出:Windows 平臺下,輸出設備是不帶緩衝區的,Linux 和 Mac OS 平臺下,輸出設備帶有行緩衝區。
2、緩衝區的刷新(清空)
所謂刷新緩衝區,就是將緩衝區中的內容送達到目的地。緩衝區的刷新遵循以下的規則:
- 不管是行緩衝還是全緩衝,緩衝區滿時會自動刷新;
- 行緩衝遇到換行符\n時會刷新;
- 關閉文件時會刷新緩衝區;
- 程序關閉時一般也會刷新緩衝區,這個是由標準庫來保障的;
- 使用特定的函數也可以手動刷新緩衝區。