C語言之輸入輸出

輸入輸出

在C語言中,有三個函數可以用來在顯示器上輸出數據,它們分別是:

puts():只能輸出字符串,並且輸出結束後會自動換行。
putchar():只能輸出單個字符。
printf():可以輸出各種類型的數據,在前面的很多章節中都進行了介紹。

printf() 是最靈活、最複雜、最常用的輸出函數,完全可以替代 puts() 和 putchar(),大家一定要掌握。前面的章節中我們已經介紹了 printf() 的基本用法,本節將重點介紹 printf() 的高級用法。

對於初學者,這一節的內容可能有些繁雜,如果你希望加快學習進度,儘早寫出有趣的代碼,也可以跳過這節,後面遇到不懂的 printf() 用法再來回顧。

首先彙總一下前面學到的格式控制符:

C語言 格式控制符 說明
%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 輸出一個字符串

數據輸出大彙總以及輕量進階

printf() 的高級用法

通過前面的學習,相信你已經熟悉了 printf() 的基本用法,但是這還不足以把它發揮到極致,printf() 可以有更加炫酷、更加個性、更加整齊的輸出形式。

假如現在老師要我們輸出一個 4×4 的整數矩陣,爲了增強閱讀性,數字要對齊,怎麼辦呢?我們顯然可以這樣做:

#include <stdio.h>
int main()
{
    int a1=20, a2=345, a3=700, a4=22;
    int b1=56720, b2=9999, b3=20098, b4=2;
    int c1=233, c2=205, c3=1, c4=6666;
    int d1=34, d2=0, d3=23, d4=23006783;
    printf("%d        %d       %d       %d\n", a1, a2, a3, a4);
    printf("%d     %d      %d     %d\n", b1, b2, b3, b4);
    printf("%d       %d       %d         %d\n", c1, c2, c3, c4);
    printf("%d        %d         %d        %d\n", d1, d2, d3, d4);
    return 0;
}

運行結果:

20        345       700       22
56720     9999      20098     2
233       205       1         6666
34        0         23        23006783

矩陣一般在大學的《高等數學》中會講到,m×n 的數字矩陣可以理解爲把 m×n 個數字擺放成 m 行 n 列的樣子。
看,這是多麼地自虐,要敲那麼多空格,還要嚴格控制空格數,否則輸出就會錯位。更加噁心的是,如果數字的位數變了,空格的數目也要跟着變。例如,當 a1 的值是 20 時,它後面要敲八個空格;當 a1 的值是 1000 時,它後面就要敲六個空格。每次修改整數的值,都要考慮修改空格的數目,逼死強迫症。

類似的需求隨處可見,整齊的格式會更加美觀,讓人覺得生動有趣。其實,我們大可不必像上面一樣,printf() 可以更好的控制輸出格式。更改上面的代碼:

#include <stdio.h>
int main()
{
    int a1=20, a2=345, a3=700, a4=22;
    int b1=56720, b2=9999, b3=20098, b4=2;
    int c1=233, c2=205, c3=1, c4=6666;
    int d1=34, d2=0, d3=23, d4=23006783;
    printf("%-9d %-9d %-9d %-9d\n", a1, a2, a3, a4);
    printf("%-9d %-9d %-9d %-9d\n", b1, b2, b3, b4);
    printf("%-9d %-9d %-9d %-9d\n", c1, c2, c3, c4);
    printf("%-9d %-9d %-9d %-9d\n", d1, d2, d3, d4);
    return 0;
}

輸出結果:

20        345       700       22
56720     9999      20098     2
233       205       1         6666
34        0         23        23006783

這樣寫起來更加方便,即使改變某個數字,也無需修改 printf() 語句,增加或者減少空格數目。

**%-9d中,d表示以十進制輸出,9表示最少佔9個字符的寬度,寬度不足以空格補齊,-表示左對齊。綜合起來,%-9d表示以十進制輸出,左對齊,寬度最小爲9個字符。**大家可以親自試試%9d的輸出效果。

printf() 格式控制符的完整形式如下:

%[flag][width][.precision]type

[ ] 表示此處的內容可有可無,是可以省略的。

1) type 表示輸出類型,

比如 %d、%f、%c、%lf,type 就分別對應 d、f、c、lf;再如,%-9d中 type 對應 d。

type 這一項必須有,這意味着輸出時必須要知道是什麼類型。

2) width 表示最小輸出寬度,

也就是至少佔用幾個字符的位置;例如,%-9d中 width 對應 9,表示輸出結果最少佔用 9 個字符的寬度。

當輸出結果的寬度不足 width 時,以空格補齊(如果沒有指定對齊方式,默認會在左邊補齊空格);當輸出結果的寬度超過 width 時,width 不再起作用,按照數據本身的寬度來輸出。

下面的代碼演示了 width 的用法:

#include <stdio.h>
int main(){
    int n = 234;
    float f = 9.8;
    char c = '@';
    char *str = "http://c.biancheng.net";
    printf("%10d%12f%4c%8s", n, f, c, str);
    return 0;
}

運行結果:

       234    9.800000   @http://c.biancheng.net

對輸出結果的說明:

n 的指定輸出寬度爲 10,234 的寬度爲 3,所以前邊要補上 7 個空格。
f 的指定輸出寬度爲 12,9.800000 的寬度爲 8,所以前邊要補上 4 個空格。
str 的指定輸出寬度爲 8,“http://c.biancheng.net” 的寬度爲 22,超過了 8,所以指定輸出寬度不再起作用,而是按照 str 的實際寬度輸出。

3) .precision 表示輸出精度,也就是小數的位數。

當小數部分的位數大於 precision 時,會按照四捨五入的原則丟掉多餘的數字;
當小數部分的位數小於 precision 時,會在後面補 0。

另外,.precision 也可以用於整數和字符串,但是功能卻是相反的:
用於整數時,.precision 表示最小輸出寬度。與 width 不同的是,整數的寬度不足時會在左邊補 0,而不是補空格。
用於字符串時,.precision 表示最大輸出寬度,或者說截取字符串。當字符串的長度大於 precision 時,會截掉多餘的字符;當字符串的長度小於 precision 時,.precision 就不再起作用。

請看下面的例子:

#include <stdio.h>
int main(){
    int n = 123456;
    double f = 882.923672;
    char *str = "abcdefghi";
    printf("n: %.9d  %.4d\n", n, n);
    printf("f: %.2lf  %.4lf  %.10lf\n", f, f, f);
    printf("str: %.5s  %.15s\n", str, str);
    return 0;
}

運行結果:

n: 000123456  123456
f: 882.92  882.9237  882.9236720000
str: abcde  abcdefghi

對輸出結果的說明:

對於 n,.precision 表示最小輸出寬度。n 本身的寬度爲 6,當 precision 爲 9 時,大於 6,要在 n 的前面補 3 個 0;當 precision 爲 4 時,小於 6,不再起作用。
對於 f,.precision 表示輸出精度。f 的小數部分有 6 位數字,當 precision 爲 2 或者 4 時,都小於 6,要按照四捨五入的原則截斷小數;當 precision 爲 10 時,大於 6,要在小數的後面補四個 0。
對於 str,.precision 表示最大輸出寬度。str 本身的寬度爲 9,當 precision 爲 5 時,小於 9,要截取 str 的前 5 個字符;當 precision 爲 15 時,大於 9,不再起作用。

4) flag 是標誌字符。

例如,%#x中 flag 對應 #,%-9d中 flags 對應-。下表列出了 printf() 可以用的 flag:

標誌字符 含 義
- -表示左對齊。如果沒有,就按照默認的對齊方式,默認一般爲右對齊。
+ 用於整數或者小數,表示輸出符號(正負號)。如果沒有,那麼只有負數纔會輸出符號。
空格 用於整數或者小數,輸出值爲正時冠以空格,爲負時冠以負號
# 對於八進制(%o)和十六進制(%x / %X)整數,# 表示在輸出時添加前綴;八進制的前綴是 0,十六進制的前綴是 0x / 0X。

注意: 對於小數(%f / %e / %g),# 表示強迫輸出小數點。如果沒有小數部分,默認是不輸出小數點的,加上 # 以後,即使沒有小數部分也會帶上小數點。

請看下面的例子:

#include <stdio.h>
int main(){
    int m = 192, n = -943;
    float f = 84.342;
    printf("m=%10d, m=%-10d\n", m, m);  //演示 - 的用法
    printf("m=%+d, n=%+d\n", m, n);  //演示 + 的用法
    printf("m=% d, n=% d\n", m, n);  //演示空格的用法
    printf("f=%.0f, f=%#.0f\n", f, f);  //演示#的用法
    return 0;
}

運行結果:

m=       192, m=192      
m=+192, n=-943
m= 192, n=-943
f=84, f=84.

對輸出結果的說明:

當以%10d輸出 m 時,是右對齊,所以在 192 前面補七個空格;當以%-10d輸出 m 時,是左對齊,所以在 192 後面補七個空格。
m 是正數,以%+d輸出時要帶上正號;n 是負數,以%+d輸出時要帶上負號。
m 是正數,以% d輸出時要在前面加空格;n 是負數,以% d輸出時要在前面加負號。
%.0f表示保留 0 位小數,也就是隻輸出整數部分,不輸出小數部分。默認情況下,這種輸出形式是不帶小數點的,但是如果有了#標誌,那麼就要在整數的後面“硬加上”一個小數點,以和純整數區分開。

printf() 不能立即輸出的問題

printf() 有一個尷尬的問題,就是有時候不能立即輸出,請看下面的代碼:

#include<stdio.h>
#include<unistd.h>
int main()
{
    printf("C語言中文網");
    sleep(5);  //程序暫停5秒鐘
    printf("http://c.biancheng.net\n");
    return 0;
}

這段代碼使用了兩個 printf() 語句,它們之間有一個 sleep() 函數,該函數的作用是讓程序暫停 5 秒,然後再繼續執行。sleep() 是 Linux 和 Mac OS 下特有的函數,不能用於 Windows。當然,Windows 下也有功能相同的暫停函數,叫做 Sleep(),稍後我們會講解。

在 Linux 或者 Mac OS 下運行該程序,會發現第一個 printf() 並沒有立即輸出,而是等待 5 秒以後,和第二個 printf() 一起輸出了.

我們不妨修改一下代碼,在第一個 printf() 的最後添加一個換行符,如下所示:
printf(“C語言中文網\n”);

再次編譯並運行程序,發現第一個 printf() 首先輸出(程序運行後立即輸出),等待 5 秒以後,第二個 printf() 才輸出.

爲什麼一個換行符\n就能讓程序的表現有天壤之別呢?按照通常的邏輯,程序運行後第一個 printf() 應該立即輸出,而不是等待 5 秒以後再和第二個 printf() 一起輸出,也就是說,第二種情形才符合我們的慣性思維。然而,第一種情形該如何理解呢?

其實,這一切都是輸出緩衝區(緩存)在作怪!

從本質上講,printf() 執行結束以後數據並沒有直接輸出到顯示器上,而是放入了緩衝區,直到遇見換行符\n纔將緩衝區中的數據輸出到顯示器上。

以上測試的是 Linux 和 Mac OS,我們不妨再測試一下 Windows,請看下面的代碼:

#include<stdio.h>
#include<Windows.h>
int main()
{
    printf("C語言中文網");
    Sleep(5000);  //程序暫停5秒鐘
    printf("http://c.biancheng.net\n");
    return 0;
}

在 Windows 下,想讓程序暫停可以使用 Windows.h 頭文件中的 Sleep() 函數(S要大寫),它和 Linux 下的 sleep() 功能相同。不過,sleep() 要求的時間單位是秒,而 Sleep() 要求的時間單位是毫秒,1 秒等於 1000 毫秒。這段代碼中,我們要求程序暫停 5000 毫秒,也即 5 秒。

編譯並運行程序,會發現第一個 printf() 首先輸出(程序運行後立即輸出),等待 5 秒以後,第二個 printf() 才輸出,請看下面的動畫演示:

在第一個 printf() 的最後添加一個換行符,情況也是一樣的,第一個 printf() 從來不會和第二個 printf() 一起輸出。

你看,Windows 和 Linux、Mac OS 的情況又不一樣。這是因爲,Windows 和 Linux、Mac OS 的緩存機制不同.

要想破解 printf() 輸出的問題,必須要了解緩存,它能使你對輸入輸出的認識上升到一個更高的層次,以後不管遇到什麼疑難雜症,都能迎刃而解。可以說,輸入輸出的“命門”就在於緩存。

總結

對於初學者來說,上面講到的 printf() 用法已經比較複雜了,基本滿足了實際開發的需求,相信大家也需要一段時間才能熟悉。但是,受到所學知識的限制,本文也未能講解 printf() 的所有功能,後續我們還會逐步深入。

printf() 的這些格式規範不是“小把戲”,優美的輸出格式隨處可見,例如,dos 下的 dir 命令,會整齊地列出當前目錄下的文件,這明顯使用了右對齊,還指定了寬度。

使用scanf從鍵盤輸入數據

程序是人機交互的媒介,有輸出必然也有輸入,前面我們講解了如何將數據輸出到顯示器上,本章我們開始講解如何從鍵盤輸入數據。在C語言中,有多個函數可以從鍵盤獲得用戶輸入:

scanf():和 printf() 類似,scanf() 可以輸入多種類型的數據。
getchar()、getche()、getch():這三個函數都用於輸入單個字符。
gets():獲取一行數據,並作爲字符串處理。

scanf() 是最靈活、最複雜、最常用的輸入函數,但它不能完全取代其他函數,大家都要有所瞭解。

本節我們只講解 scanf(),其它的輸入函數將在下節講解。

scanf()函數

scanf 是 scan format 的縮寫,意思是格式化掃描,也就是從鍵盤獲得用戶輸入,和 printf 的功能正好相反。

我們先來看一個例子:

#include <stdio.h>
int main()
{
    int a = 0, b = 0, c = 0, d = 0;
    scanf("%d", &a);  //輸入整數並賦值給變量a
    scanf("%d", &b);  //輸入整數並賦值給變量b
    printf("a+b=%d\n", a+b);  //計算a+b的值並輸出
    scanf("%d %d", &c, &d);  //輸入兩個整數並分別賦值給c、d
    printf("c*d=%d\n", c*d);  //計算c*d的值並輸出
    return 0;
}

運行結果:

12↙
60↙
a+b=72
10 23↙
c*d=230

↙表示按下回車鍵。
從鍵盤輸入12,按下回車鍵,scanf() 就會讀取輸入數據並賦值給變量 a;本次輸入結束,接着執行下一個 scanf() 函數,再從鍵盤輸入 60,按下回車鍵,就會將 60 賦值給變量 b,都是同樣的道理。

第 8 行代碼中,scanf() 有兩個以空格分隔的%d,後面還跟着兩個變量,這要求我們一次性輸入兩個整數,並分別賦值給 c 和 d。注意"%d %d"之間是有空格的,所以輸入數據時也要有空格。對於 scanf(),輸入數據的格式要和控制字符串的格式保持一致。

其實 scanf 和 printf 非常相似,只是功能相反罷了:

scanf("%d %d", &a, &b);  // 獲取用戶輸入的兩個整數,分別賦值給變量 a 和 b
printf("%d %d", a, b);  // 將變量 a 和 b 的值在顯示器上輸出

它們都有格式控制字符串,都有變量列表。不同的是,scanf 的變量前要帶一個&符號。&稱爲取地址符,也就是獲取變量在內存中的地址。

我們已經知道數據是以二進制的形式保存在內存中的,字節(Byte)是最小的可操作單位。爲了便於管理,我們給每個字節分配了一個編號,使用該字節時,只要知道編號就可以,就像每個學生都有學號,老師會隨機抽取學號來讓學生回答問題。字節的編號是有順序的,從 0 開始,接下來是 1、2、3……

下圖是 4G 內存中每個字節的編號(以十六進制表示):

這個編號,就叫做地址(Address)。int a;會在內存中分配四個字節的空間,我們將第一個字節的地址稱爲變量 a 的地址,也就是&a的值。對於前面講到的整數、浮點數、字符,都要使用 & 獲取它們的地址,scanf 會根據地址把讀取到的數據寫入內存。

我們不妨將變量的地址輸出看一下:

#include <stdio.h>
int main()
{
    int a='F';
    int b=12;
    int c=452;
    printf("&a=%p, &b=%p, &c=%p\n", &a, &b, &c);
   return 0;
}

輸出結果:

&a=0x18ff48, &b=0x18ff44, &c=0x18ff40

%p是一個新的格式控制符,它表示以十六進制的形式(帶小寫的前綴)輸出數據的地址。如果寫作%P,那麼十六進制的前綴也將變成大寫形式。

圖:a、b、c 的內存地址
注意:這裏看到的地址都是假的,是虛擬地址,並不等於數據在物理內存中的地址。虛擬地址是現代計算機因內存管理的需要才提出的概念
再來看一個 scanf 的例子:

#include <stdio.h>
int main()
{
    int a, b, c;
    scanf("%d %d", &a, &b);
    printf("a+b=%d\n", a+b);
    scanf("%d   %d", &a, &b);
    printf("a+b=%d\n", a+b);
    scanf("%d, %d, %d", &a, &b, &c);
    printf("a+b+c=%d\n", a+b+c);
   
    scanf("%d is bigger than %d", &a, &b);
    printf("a-b=%d\n", a-b);
    return 0;
}

運行結果:

10    20↙
a+b=30
100 200↙
a+b=300
56,45,78↙
a+b+c=179
25 is bigger than 11↙
a-b=14

第一個 scanf() 的格式控制字符串爲"%d %d",中間有一個空格,而我們卻輸入了10 20,中間有多個空格。第二個 scanf() 的格式控制字符串爲"%d %d",中間有多個空格,而我們卻輸入了100 200,中間只有一個空格。這說明 scanf() 對輸入數據之間的空格的處理比較寬鬆,並不要求空格數嚴格對應,多幾個少幾個無所謂,只要有空格就行。

第三個 scanf() 的控制字符串爲"%d, %d, %d",中間以逗號分隔,所以輸入的整數也要以逗號分隔。

第四個 scanf() 要求整數之間以is bigger than分隔。

用戶每次按下回車鍵,程序就會認爲完成了一次輸入操作,scanf() 開始讀取用戶輸入的內容,並根據格式控制字符串從中提取有效數據,只要用戶輸入的內容和格式控制字符串匹配,就能夠正確提取。

本質上講,用戶輸入的內容都是字符串,scanf() 完成的是從字符串中提取有效數據的過程。

連續輸入

在本節第一段示例代碼中,我們一個一個地輸入變量 a、b、c、d 的值,每輸入一個值就按一次回車鍵。現在我們改變輸入方式,將四個變量的值一次性輸入,如下所示:

12 60 10 23↙
a+b=72
c*d=230

可以發現,兩個 scanf() 都能正確讀取。合情合理的猜測是,第一個 scanf() 讀取完畢後沒有拋棄多餘的值,而是將它們保存在了某個地方,下次接着使用。

如果我們多輸入一個整數,會怎樣呢?

12 60 10 23 99↙
a+b=72
c*d=230

這次我們多輸入了一個 99,發現 scanf() 仍然能夠正確讀取,只是 99 沒用罷了。

如果我們少輸入一個整數,又會怎樣呢?

12 60 10↙
a+b=72
23↙
c*d=230

輸入三個整數後,前兩個 scanf() 把前兩個整數給讀取了,剩下一個整數 10,而第三個 scanf() 要求輸入兩個整數,一個單獨的 10 並不能滿足要求,所以我們還得繼續輸入,湊夠兩個整數以後,第三個 scanf() 才能讀取完畢。

從本質上講,我們從鍵盤輸入的數據並沒有直接交給 scanf(),而是放入了緩衝區中,直到我們按下回車鍵,scanf() 纔到緩衝區中讀取數據。如果緩衝區中的數據符合 scanf() 的要求,那麼就讀取結束;如果不符合要求,那麼就繼續等待用戶輸入,或者乾脆讀取失敗。

注意,如果緩衝區中的數據不符合 scanf() 的要求,要麼繼續等待用戶輸入,要麼就乾脆讀取失敗,上面我們演示了“繼續等待用戶輸入”的情形,下面我們對代碼稍作修改,演示“讀取失敗”的情形。

#include <stdio.h>
int main()
{
    int a = 1, b = 2, c = 3, d = 4;  //修改處:給變量賦予不同的初始值
    scanf("%d", &a);
    scanf("%d", &b);
    printf("a=%d, b=%d\n", a, b);
    scanf("%d %d", &c, &d);
    printf("c=%d, d=%d\n", c, d);
   
    return 0;
}

運行結果:

12 60 a10↙
a=12, b=60
c=3, d=4

前兩個整數被正確讀取後,剩下了 a10,而第三個 scanf() 要求輸入兩個十進制的整數,a10 無論如何也不符合要求,所以只能讀取失敗。輸出結果也證明了這一點,c 和 d 的值並沒有被改變。

這說明 scanf() 不會跳過不符合要求的數據,遇到不符合要求的數據會讀取失敗,而不是再繼續等待用戶輸入。

總而言之,正是由於緩衝區的存在,才使得我們能夠多輸入一些數據,或者一次性輸入所有數據,這可以認爲是緩衝區的一點優勢。然而,緩衝區也帶來了一定的負面影響,甚至會導致很奇怪的行爲,請看下面的代碼:

#include <stdio.h>
int main()
{
    int a = 1, b = 2;
    scanf("a=%d", &a);
    scanf("b=%d", &b);
    printf("a=%d, b=%d\n", a, b);
    return 0;
}

輸入示例:

a=99↙
a=99, b=2

輸入a=99,按下回車鍵,程序竟然運行結束了,只有第一個 scanf() 成功讀取了數據,第二個 scanf() 彷彿沒有執行一樣,根本沒有給用戶任何機會去輸入數據。

如果我們換一種輸入方式呢?

a=99b=200↙
a=99, b=200

這樣 a 和 b 都能夠正確讀取了。注意,a=99b=200中間是沒有任何空格的。

肯定有好奇的小夥伴又問了,如果a=99b=200兩個數據之間有空格又會怎麼樣呢?我們不妨親試一下:

a=99 b=200↙
a=99, b=2

你看,第二個 scanf() 又讀取失敗了!在前面的例子中,輸入的兩份數據之前都是有空格的呀,爲什麼這裏不能帶空格呢,真是匪夷所思。好吧,這個其實還是跟緩衝區有關係。

要想破解 scanf() 輸入的問題,一定要學習緩衝區,它能使你對輸入輸出的認識上升到一個更高的層次,以後不管遇到什麼疑難雜症,都能迎刃而解。可以說,輸入輸出的“命門”就在於緩衝區。

輸入其它數據

除了輸入整數,scanf() 還可以輸入單個字符、字符串、小數等,請看下面的演示:

#include <stdio.h>
int main()
{
    char letter;
    int age;
    char url[30];
    float price;
   
    scanf("%c", &letter);
    scanf("%d", &age);
    scanf("%s", url); //可以加&也可以不加&
    scanf("%f", &price);
   
    printf("26個英文字母的最後一個是 %c。\n", letter);
    printf("C語言中文網已經成立%d年了,網址是 %s,開通VIP會員的價格是%g。\n", age, url, price);
    return 0;
}

運行示例:

z↙
6↙
http://c.biancheng.net↙
159.9↙
26個英文字母的最後一個是 z。
C語言中文網已經成立6年了,網址是 http://c.biancheng.net,開通VIP會員的價格是159.9。

scanf() 和 printf() 雖然功能相反,但是格式控制符是一樣的,單個字符、整數、小數、字符串對應的格式控制符分別是 %c、%d、%f、%s。
對讀取字符串的說明
在字符串的兩種定義形式,它們分別是:

char str1[] = "http://c.biancheng.net";
char *str2 = "C語言中文網";

這兩種形式其實是有區別的,第一種形式的字符串所在的內存既有讀取權限又有寫入權限,第二種形式的字符串所在的內存只有讀取權限,沒有寫入權限。printf()、puts() 等字符串輸出函數只要求字符串有讀取權限,而 scanf()、gets() 等字符串輸入函數要求字符串有寫入權限,所以,第一種形式的字符串既可以用於輸出函數又可以用於輸入函數,而第二種形式的字符串只能用於輸出函數。

另外,對於第一種形式的字符串,在[ ]裏面要指明字符串的最大長度,如果不指明,也可以根據=後面的字符串來自動推算,此處,就是根據"http://c.biancheng.net"的長度來推算的。但是在前一個例子中,開始我們只是定義了一個字符串,並沒有立即給它賦值,所以沒法自動推算,只能手動指明最大長度,這也就是爲什麼一定要寫作char url[30],而不能寫作char url[]的原因。

讀者還要注意第 11 行代碼,這行代碼用來輸入字符串。上面我們說過,scanf() 讀取數據時需要的是數據的地址,整數、小數、單個字符都要加&取地址符,這很容易理解;但是對於此處的 url 字符串,我們並沒有加 &,這是因爲,字符串的名字會自動轉換爲字符串的地址,所以不用再多此一舉加 & 了。當然,你也可以加上,這樣雖然不會導致錯誤,但是編譯器會產生警告.

關於字符串,後續章節我們還會專門講解,這裏只要求大家會模仿,不要徹底理解,也沒法徹底理解。

最後需要注意的一點是,scanf() 讀取字符串時以空格爲分隔,遇到空格就認爲當前字符串結束了,所以無法讀取含有空格的字符串,請看下面的例子:

#include <stdio.h>
int main()
{
    char author[30], lang[30], url[30];
    scanf("%s %s", author, lang);
    printf("author:%s \nlang: %s\n", author, lang);
    scanf("%s", url);
    printf("url: %s\n", url);
    return 0;
}

運行示例:

YanChangSheng C-Language↙
author:YanChangSheng
lang: C-Language
http://c.biancheng.net http://biancheng.net↙
url: http://c.biancheng.net

對於第一個 scanf(),它將空格前邊的字符串賦值給 author,將空格後邊的字符串賦值給 lang;很顯然,第一個字符串遇到空格就結束了,第二個字符串到了本行的末尾結束了。

或許第二個 scanf() 更能說明問題,我們輸入了兩個網址,但是 scanf() 只讀取了一個,就是因爲這兩個網址以空格爲分隔,scanf() 遇到空格就認爲字符串結束了,不再繼續讀取了。

scanf() 格式控制符彙總

格式控制符 說明
%c 讀取一個單一的字符
%hd、%d、%ld 讀取一個十進制整數,並分別賦值給 short、int、long 類型
%ho、%o、%lo 讀取一個八進制整數(可帶前綴也可不帶),並分別賦值給 short、int、long 類型
%hx、%x、%lx 讀取一個十六進制整數(可帶前綴也可不帶),並分別賦值給 short、int、long 類型
%hu、%u、%lu 讀取一個無符號整數,並分別賦值給 unsigned short、unsigned int、unsigned long 類型
%f、%lf 讀取一個十進制形式的小數,並分別賦值給 float、double 類型
%e、%le 讀取一個指數形式的小數,並分別賦值給 float、double 類型
%g、%lg 既可以讀取一個十進制形式的小數,也可以讀取一個指數形式的小數,並分別賦值給 float、double 類型
%s 讀取一個字符串(以空白符爲結束)

輸入字符和字符串

輸入單個字符

輸入單個字符當然可以使用 scanf() 這個通用的輸入函數,對應的格式控制符爲%c,上節已經講到了。本節我們重點講解的是 getchar()、getche() 和 getch() 這三個專用的字符輸入函數,它們具有某些 scanf() 沒有的特性,是 scanf() 不能代替的。

1) getchar()

最容易理解的字符輸入函數是 getchar(),它就是scanf("%c", c)的替代品,除了更加簡潔,沒有其它優勢了;或者說,getchar() 就是 scanf() 的一個簡化版本。

下面的代碼演示了 getchar() 的用法:

#include <stdio.h>
int main()
{
    char c;
    c = getchar();
    printf("c: %c\n", c);
    return 0;
}

輸入示例:

@↙
c: @

你也可以將第 4、5 行的語句合併爲一個,從而寫作:
char c = getchar();

2) getche()

getche() 就比較有意思了,它沒有緩衝區,輸入一個字符後會立即讀取,不用等待用戶按下回車鍵,這是它和 scanf()、getchar() 的最大區別。請看下面的代碼:

#include <stdio.h>
#include <conio.h>
int main()
{
    char c = getche();
    printf("c: %c\n", c);
    return 0;
}

輸入示例:
@c: @

輸入@後,getche() 立即讀取完畢,接着繼續執行 printf() 將字符輸出,所以沒有按下回車鍵程序就運行結束了。

注意,getche() 位於 conio.h 頭文件中,而這個頭文件是 Windows 特有的,Linux 和 Mac OS 下沒有包含該頭文件。換句話說,getche() 並不是標準函數,默認只能在 Windows 下使用,不能在 Linux 和 Mac OS 下使用。

3) getch()

getch() 也沒有緩衝區,輸入一個字符後會立即讀取,不用按下回車鍵,這一點和 getche() 相同。getch() 的特別之處是它沒有回顯,看不到輸入的字符。所謂回顯,就是在控制檯上顯示出用戶輸入的字符;沒有回顯,就不會顯示用戶輸入的字符,就好像根本沒有輸入一樣。

回顯在大部分情況下是有必要的,它能夠與用戶及時交互,讓用戶清楚地看到自己輸入的內容。但在某些特殊情況下,我們卻不希望有回顯,例如輸入密碼,有回顯是非常危險的,容易被偷窺。

getch() 使用舉例:

#include <stdio.h>
#include <conio.h>
int main()
{
    char c = getch();
    printf("c: %c\n", c);
    return 0;
}

輸入@後,getch() 會立即讀取完畢,接着繼續執行 printf() 將字符輸出。但是由於 getch() 沒有回顯,看不到輸入的@字符,所以控制檯上最終顯示的內容爲c: @

注意,和 getche() 一樣,getch() 也位於 conio.h 頭文件中,也不是標準函數,默認只能在 Windows 下使用,不能在 Linux 和 Mac OS 下使用。

輸入字符串

輸入字符串當然可以使用 scanf() 這個通用的輸入函數,對應的格式控制符爲%s,上節已經講到了;本節我們重點講解的是 gets() 這個專用的字符串輸入函數,它擁有一個 scanf() 不具備的特性。

gets() 的使用也很簡單,請看下面的代碼:

#include <stdio.h>
int main()
{
    char author[30], lang[30], url[30];
    gets(author);
    printf("author: %s\n", author);
    gets(lang);
    printf("lang: %s\n", lang);
    gets(url);
    printf("url: %s\n", url);
   
    return 0;
}

運行結果:

YanChangSheng↙
author: YanChangSheng
C-Language↙
lang: C-Language
http://c.biancheng.net http://biancheng.net↙
url: http://c.biancheng.net http://biancheng.net

gets() 是有緩衝區的,每次按下回車鍵,就代表當前輸入結束了,gets() 開始從緩衝區中讀取內容,這一點和 scanf() 是一樣的。gets() 和 scanf() 的主要區別是:

  1. scanf() 讀取字符串時以空格爲分隔,遇到空格就認爲當前字符串結束了,所以無法讀取含有空格的字符串。
  2. gets() 認爲空格也是字符串的一部分,只有遇到回車鍵時才認爲字符串輸入結束,所以,不管輸入了多少個空格,只要不按下回車鍵,對 gets() 來說就是一個完整的字符串。

也就是說**,gets() 能讀取含有空格的字符串,而 scanf() 不能**。

總結

  1. C語言中常用的從控制檯讀取數據的函數有五個,它們分別是 scanf()、getchar()、getche()、getch() 和 gets()。其中 scanf()、getchar()、gets() 是標準函數,適用於所有平臺;getche() 和 getch() 不是標準函數,只能用於 Windows。

  2. scanf() 是通用的輸入函數,它可以讀取多種類型的數據。

  3. getchar()、getche() 和 getch() 是專用的字符輸入函數,它們在緩衝區和回顯方面與 scanf() 有着不同的特性,是 scanf() 不能替代的。

  4. gets() 是專用的字符串輸入函數,與 scanf() 相比,gets() 的主要優勢是可以讀取含有空格的字符串。

  5. scanf() 可以一次性讀取多份類型相同或者不同的數據,getchar()、getche()、getch() 和 gets() 每次只能讀取一份特定類型的數據,不能一次性讀取多份數據。

c語言魅力真的很大,體驗一下linux下的c語言編程更爽.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章