我們前面學習過輸出,printf
函數,標準庫提供了一次讀/寫一個字符的函數,其中最簡單的是getchar
和putchar
,顧名思義,getchar
為得到一個字符,putchar
輸出一個字符。每次調用時,getchar
函數從輸入流中讀取一個字符,并將其作為結果值返回,例如:
int c = getchar()
變量c中包含輸入流中的下一個字符,暫時是從鍵盤輸入。每次調用putchar()
時將打印一個字符,例如
putchar(c)
將把整型變量c的內容以字符的形式打印出來,通常是顯示在屏幕上
1.5.1 字符復制
下面這個程序為復制輸入,然后輸出
#include <stdio.h>
/* copy input to output; 1st version */
int main()
{
int c;
c = getchar();
while (c != EOF) {
putchar(c);
c = getchar();
}
return 0;
}
其中,關系運算符!=
表示"不等于"。
char
類型專門用于存儲字符型數據,占用一個字節,而任何整數型int
也可以用于存儲字符型數據,因為int
一般占用四個字節,不會溢出,因為某些潛在的重要原因,我們再次使用int
類型。
在上例中,有一個特殊的字符常量EOF
(end of file,文件結束),表示輸入流的結束。在輸入流結束或是文件流結束,getchar
函數將會返回一個特殊值,與任何實際字符都不同,就是EOF
。我們在聲明變量c
的時候,必須讓它大到足以存放getchar
函數返回的任何值。之所以不把c聲明成char
,是因為它必須足夠大,除了能存儲任何可能的字符外還要能存儲文件結束符EOF
。因此必須將c
聲明稱int
類型。EOF
在頭文件<stdio.h>
是個整形數-1
,具體數值并不重要,只要它與任何char
類型的值都不相同即可。這里使用符號常量,可以確保程序不需要依賴對應的任何特定的數值。
可以將程序精煉如下:
#include <stdio.h>
/* copy input to output; 2nd version */
int main()
{
int c;
while ((c = getchar()) != EOF)
putchar(c);
}
在該程序中,while循環語句首先讀一個字符并將其賦值給c
,然后測試是否為EOF
,如果字符不是EOF
,則執行循環體,然后重復執行while
語句。當到達結尾位置(EOF)
,while
循環語句終止執行。
值得注意的是,在while ((c = getchar()) != EOF)
中,c=getchar()
兩端的小括號不可省略,因為關系運算符!=
的優先級比賦值運算符=
高。
1.5.2 字符計數
下列程序用于對字符進行計數
#include <stdio.h>
/* count characters in input; 1st version */
int main()
{
long nc;
nc = 0;
while (getchar() != EOF)
++nc;
printf("%ld\n", nc);
return 0;
}
其中循環體++nc
引入了一個新的運算符++
,其功能是執行加1操作,叫做自增操作符,可以用nc = nc + 1
代替它。與自增操作符對應的為自減操作符--
。++
與--
這兩個運算符既可以作為前綴操作符(++nc
),也可以作為后綴操作符(nc++
),關于這兩者的區別,我們以后介紹,當然你也可以自行學習。
該字符計數程序使用long
類型的變量存放計數值,而不是使用int
,是因為long
類型要比int
大,至少會占用32位,在某些機器上,int
占用16位,所以相當小的輸入都可能使int
類型的變量溢出。%ld
指示printf
函數對應的參數是long
類型。
使用double
(雙精度浮點型)類型可以處理更大的數字。我們用double
和for
循環語句來展示編寫此循環的另一種方式:
#include <stdio.h>
/* count characters in input; 2nd version */
int main()
{
double nc;
for (nc = 0; getchar() != EOF; ++nc)
;
printf("%.0f\n", nc);
}
對于float
與double
類型,printf
函數都使用%f
進行說明。%.0f
表示不打印小數點和小數部分。
這段程序比較特殊的是我們的循環體是空的。我們的任務都在條件測試部分和增加步長部分完成了,但是for
循環必須有一個循環體,所以我們寫一條空語句;
。單獨的分號稱為空語句。
1.5.3 行計數
接下來的這個程序用于統計輸入中的行數,判斷行數的方法就是看\n
的個數
#include <stdio.h>
/* count lines in input */
int main()
{
int c, nl;
nl = 0;
while ((c = getchar()) != EOF)
if (c == '\n')
++nl;
printf("%d\n", nl);
}
在該程序中,while
循環語句的循環體是一個if
語句,它控制自增語句++nl
。if
語句先測試圓括號的條件,如果為真,就執行后面的一條語句或是大括號括起來的一組語句,這里用縮進來表示語句之間的控制關系。
雙等于號==
是C語言中表示"關系"的運算符。記住:=
為賦值運算符,而==
為是否相等的關系運算符,切記!
用單引號' \n'
擴住的字符表示一個整形數,數值等于此字符在機器字符集中對應的數值,我們成為字符常量。這是較小的整形數的另一種寫法。比如:'A'
是一個字符常量,在ASCII
字符集中其值為65。當然,用'A'要比用65好,以為,'A'的意義更加清晰。
字符串常量中使用的轉義字符序列也是合法的字符常量,比如,'\n'代表換行符的值,在'ASCII'字符集中其值為10,應對注意,'\n'是單個字符,在表達式中不過是一個整形數而已,而"\n"
是一個僅包含一個字符的字符串常量。
1.5.4 單詞計數
我們介紹的這個程序用于統計行數、單詞數與字符數。這里單詞的定義為,不含空格、制表符或換行符的字符序列。
#include <stdio.h>
#define IN 1 /* inside a word */
#define OUT 0 /* outside a word */
/* count lines, words, and characters in input */
int main()
{
int c, nl, nw, nc, state;
state = OUT;
nl = nw = nc = 0;
while ((c = getchar()) != EOF) {
++nc;
if (c == '\n')
++nl;
if (c == ' ' || c == '\n' || c == '\t')
state = OUT;
else if (state == OUT) {
state = IN;
++nw;
}
}
printf("%d %d %d\n", nl, nw, nc);
return 0;
}
程序執行時,每當遇見單詞的第一個字符,它就作為一個新單詞加以統計。state變量記錄程序當前是否正正位于一個單詞中,它的處置為OUT
(不在單詞中),我們在這里使用了符號常量IN
與OUT
,而沒有使用1與0,這樣程序更易讀。
以下語句
nl = nw = nc = 0;
這種賦值方式為聯合賦值方式,由于賦值運算符=
的結合性為右結合,所以等價于:
n1 = (nw = (nc = 0));
運算符||
代表OR(邏輯或),所以以下語句
if (c == ' ' || c== '\n' || c == '\t')
的意義為"如果c是空格,或換行符,或制表符",與之對應的,運算符&&
代表AND(邏輯與),它比||
高一個優先級。由&&
或||
連接的表達式由左至右求值,并在求值過程中只要能判斷出最終結果為真或假,就立即終止。這種特性稱為短路。比如:
a = 1,b = 2;
if(a == 1 || b == 1){
printf("a為1或b為1")
}
||
只要有一個真,整個表達式的值就為真,所以比較完a==1
,即可求出表達式的值為真,所以b==1
就不會比較。
這段程序中還包括一個else
部分,它指定if
語句中的條件部分為假時所要執行的動作,一般形式為:
if(表達式)
語句1;
else
語句2;
if
后的條件為真執行語句1,為假執行語句2,語句1和語句2都可以是單條語句或是大括號括起來的多條語句。