C語言學習筆記(一):數據類型,運算符與表達式,控制語句

目錄

1 介紹

2 基本數據類型

3 字符串

4 運算符與表達式

5 控制語句

6 I/O


1 介紹

1 C語言編譯過程

鏈接器的作用是,把你編寫的目標代碼、系統的標準啓動代碼和庫代碼這 3 部分合併成一個文件,即可執行文件。對於庫代碼,鏈接器只會把程序中要用到的庫函數代碼提取出來。

2 C程序解剖

3 命名規則

給變量命名時要使用有意義的變量名或標識符,如果變量名無法清楚地表達自身的用途,可在註釋中進一步說明。

可以用小寫字母、大寫字母、數字和下劃線(_)來命名。而且,名稱的第1個字符必須是字符或下劃線,不能是數字。

操作系統和C庫經常使用以一個或兩個下劃線字符開始的標識符(如,_kcab),因此最好避免在自己的程序中使用這種名稱。

C語言的名稱區分大小寫,即把一個字母的大寫和小寫視爲兩個不同的字符。因此,stars和Stars、STARS都不同。

C99和C11允許使用更長的標識符名,但是編譯器只識別前63個字符。對於外部標識符,只允許使用31個字符。

 

2 基本數據類型

在C語言中,整數是沒有小數部分的數。浮點數與數學中實數的概念差不多,2.75、3.16E7、7.00 和 2e-8 都是浮
點數。注意,在一個值後面加上一個小數點,該值就成爲一個浮點值。所以,7是整數,7.00是浮點數。

計算機把浮點數分成小數部分和指數部分來表示,而且分開儲存這兩部分。

2.1 基本整型:int

int類型是有符號整型,即int類型的值必須是整數,可以是正整數、負整數或零。其取值範圍依計算機系統而異。一般而言,儲存一個int要佔用一個機器字長。目前的個人計算機一般是64位,因此用64位儲存一個int值。

2.2 其他整型

C語言提供3個附屬關鍵字修飾基本整數類型:short、long和unsigned

有符號類型:

       short  int類型(或者簡寫爲short)佔用的存儲空間可能比int類型少,常用於較小數值的場合以節省空間。

       long int或long佔用的存儲空間可能比int多,適用於較大數值的場合。

       long long int或long long(C99標準加入)佔用的儲存空間可能比long多,適用於更大數值的場合。該類型至少佔64位。

無符號類型:
      unsigned  int或unsigned只用於非負值的場合。這種類型與有符號類型表示的範圍不同。

八進制和十六進制常量被視爲int類型。如果值太大,編譯器會嘗試使用unsigned int。如果還不夠大,編譯器會依次使用long、unsigned long、longlong和unsigned long long類型。

要把一個較小的常量作爲long類型對待,可以在值的末尾加上l(小寫的L)或L後綴。類似地,在支持long long類型的系統中,也可以使用ll或LL後綴來表示。另外,u或U後綴表示unsigned。

打印unsigned  int類型的值,使用%u轉換說明;打印long類型的值,使用%ld轉換說明。

C語言只規定了short佔用的存儲空間不能多於int,long佔用的存儲空間不能少於int。這樣規定是爲了適應不同的機器。

int類型那麼多,應該如何選擇?首先,考慮unsigned類型。這種類型的數常用於計數,因爲計數不用負數。而且,unsigned類型可以表示更大的正數。如果一個數超出了int類型的取值範圍,且在long類型的取值範圍內時,使用long類型。、

如果整數超出了相應類型的取值範圍會怎樣?可以把無符號整數看作是汽車的里程錶。當達到它能表示的最大值
時,會重新從起始點開始。整數  也是類似的情況。它們主要的區別是,在超過最大值時,unsigned int 類型的變量 從 0開始;而int類型的變量則從−2147483648開始。

2.3 char類型

char類型用於儲存字符,但是從技術層面看,char是整數類型。因爲char類型實際上儲存的是整數而不是字符。計算機使用數字編碼來處理字符,即用特定的整數表示特定的字符。

在C語言中,用單引號括起來的單個字符被稱爲字符常量(character constant)。

char broiled;  /* 聲明一個char類型的變量 */
broiled = 'T';  /* 爲其賦值,正確 */
broiled = T;   /* 錯誤!此時T是一個變量 */
broiled = "T";  /* 錯誤!此時"T"是一個字符串 */

單引號只適用於字符、數字和標點符號,瀏覽ASCII表會發現,有些ASCII字符打印不出來。例如,一些代表行爲的字符(如,退格、換行、終端響鈴或蜂鳴)。C語言提供了3種方法表示這些字符:

第1種方法——使用ASCII碼。例如,蜂鳴字符的ASCII值是7,因此可以這樣寫:
char beep = 7;

第2種方法是,使用特殊的符號序列表示一些特殊的字符。這些符號序列叫作轉義序列(escape sequence)。
把轉義序列賦給字符變量時,必須用單引號把轉義序列括起來。例如,
char beep = '\a';

第三種方法,使用八進制和十六進制
用八進制ASCII碼錶示一個字符,可以在編碼值前面加一個反斜槓(\)並用單引號括起來。
例如,如果編譯器不識別警報字符(\a),可以使用ASCII碼來代替:beep = '\007';
用十六進制形式表示字符常量,即反斜槓後面跟一個x或X,再加上1~3位十六進制數字。
例如,Ctrl+P字符的ASCII十六進制碼是10(相當於十進制的16),可表示爲'\x10'或'\x010'。

無論是普通字符還是轉義序列,只要是雙引號括起來的字符集合,就無需再用單引號括起來。

打印輸出:

2.4 浮點型

C標準規定,float類型必須至少能表示6位有效數字,且取值範圍至少是10 -37 ~10 +37 。前一項規定指float類型必須至少精確表示小數點後的6位有效。後一項規定用於方便地表示諸如太陽質量(2.0e30千克)。通常,系統儲存一個浮點數要佔用32位。其中8位用於表示指數的值和符號,剩下24位用於表示非指數部分(也叫作尾數或有效數)及其符號。

C語言提供的另一種浮點類型是double(意爲雙精度)。double類型和float類型的最小取值範圍相同,但至少必須能表示10位有效數字。一般情況下,double佔用64位而不是32位。

浮點型常量的基本形式是:有符號的數字(包括小數點),後面緊跟e或E,最後是一個有符號數表示10的指數。

可以沒有小數點(如,2E5)或指數部分(如,19.28),但是不能同時省略兩者。
可以省略小數部分(如,3.E16)或整數部分(如,.45E-6),但是不能同時省略兩者。
不要在浮點型常量中間加空格:1.56 E+12(錯誤!)

默認情況下,編譯器假定浮點型常量是double類型的精度。

例如,假設some是float類型的變量,編寫下面的語句:
some = 4.0 * 2.0;
通常,4.0和2.0被儲存爲64位的double類型,使用雙精度進行乘法運算,然後將乘積截斷成float類型的寬度。

C99 標準添加了一種新的浮點型常量格式——用十六進制表示浮點型常量,即在十六進制數前加上十六進制前綴(0x或0X),用p和P分別代替e和E,用2的冪代替10的冪(即,p計數法)。

0xa.1fp10
十六進制a等於十進制10,.1f是1/16加上15/256(十六進制f等於十進制15),
p10是2^10 或1024。0xa.1fp10表示的值是(10  +  1/16  +15/256)×1024(即,十進制10364.0)。

printf()函數使用%f轉換說明打印十進制記數法的float和double類型浮點數,用%e打印指數記數法的浮點數。如果系統支持十六進制格式的浮點數,可用a和A分別代替e和E。打印long double類型要使用%Lf、%Le或%La。

C編譯器把浮點數轉換成整數時,會直接丟棄(截斷)小數部分,而不進行四捨五入。

2.5 類型轉換

1)當類型轉換出現在表達式時,無論是unsigned還是signed的char和short都會被自動轉換成int,如有必要會被轉換成unsigned int(如果short與int的大小相同,unsigned  short就比int大。這種情況下,unsigned  short會被轉換成unsigned int)。由於都是從較小類型轉換爲較大類型,所以這些轉換被稱爲升級(promotion)。

2)涉及兩種類型的運算,兩個值會被分別轉換成兩種類型的更高級別。

3)類型的級別從高至低依次是long  double、double、float、unsignedlonglong、long long、unsigned long、long、unsigned int、int。例外的情況是,當long 和 int 的大小相同時,unsigned int比long的級別高。之所以short和char類型沒有列出,是因爲它們已經被升級到int或unsigned int。

4)在賦值表達式語句中,計算的最終結果會被轉換成被賦值變量的類型。

5)當作爲函數參數傳遞時,char和short被轉換成int,float被轉換成double。

如果待轉換的值與目標類型不匹配怎麼辦?

1.目標類型是無符號整型,且待賦的值是整數時,額外的位將被忽略。例如,如果目標類型是 8 位unsigned char,待賦的值是原始值求模256。
2.如果目標類型是一個有符號整型,且待賦的值是整數,結果因實現而異。
3.如果目標類型是一個整型,且待賦的值是浮點數,該行爲是未定義的。
如果把一個浮點值轉換成整數類型會怎樣?當浮點類型被降級爲整數類型時,原來的浮點值會被截斷。

強制類型轉換運算符,在某個量的前面放置用圓括號括起來的類型名,該類型名即是希望轉換成的目標類型。

3 字符串

3.1 字符串

C語言沒有專門用於儲存字符串的變量類型,字符串都被儲存在char類型的數組中。數組末尾位置的字符\0。這是空字符(null  character),C語言用它標記字符串的結束。

你不用親自把空字符放入字符串末尾,scanf()在讀取輸入時就已完成這項工作。

scanf("%s", name);
如果你輸入“Angela  Plains”
scanf()只讀取了Angela  Plains中的Angela,它在遇到第1個空白(空格、製表符或換行符)時就不再讀取輸入。
因此,scanf()在讀到Angela和Plains之間的空格時就停止了。
一般而言,根據%s轉換說明,scanf()只會讀取字符串中的一個單詞,而不是一整句。

字符串常量"x"和字符常量'x'不同。區別之一在於'x'是基本類型(char),而"x"是派生類型(char數組);區別之二是"x"實際上由兩個字符組成:'x'和空字符\0。

 sizeof 運算符,它以字節爲單位給出對象的大小。strlen()函數給出字符串中的字符長度。因爲 1 字節儲存一個字符,讀者可能認爲把兩種方法應用於字符串得到的結果相同,但事實並非如此。用 strlen()得出的是字符串中的字符數(包括空格和標
點符號)。然而,sizeof運算符給出的數更大,因爲它把字符串末尾不可見的空字符也計算在內。對於早期的C,還要知道sizeof和strlen()返回的實際類型(通常是unsigned或unsigned long)。

sizeof 是否使用了圓括號

圓括號的使用時機否取決於運算對象是類型還是特定量。

運算對象是類型時,圓括號必不可少,對於類型,應寫成sizeof(char)或sizeof(float);
但是對於特定量,可有可無。對於特定量,可寫成sizeof name或sizeof 6.28。

3.2 常量和C預處理器

使用符號常量(symbolic  constant):首先,常量名比數字表達的信息更多。另外,假設程序中的多處使用一個常量,有時需要改變它的值。如何創建符號常量?

1)是聲明一個變量,然後將該變量設置爲所需的常量。可以這樣寫:

float taxrate;
taxrate = 0.015;

2)C預處理器:#define TAXRATE 0.015

編譯程序時,程序中所有的TAXRATE都會被替換成0.015。這一過程被稱爲編譯時替換(compile-time substitution)。

爲什麼TAXRATE 要用大寫?用大寫表示符號常量是 C 語言一貫的傳統。另外,還有一個不常用的命名約定,即在名稱前帶c_或k_前綴來表示常量(如,c_level或k_line)。

3)C90標準新增了const關鍵字,用於限定一個變量爲只讀,即不可以更改。

C頭文件limits.h和float.h分別提供了與整數類型和浮點類型大小限制相關的詳細信息。每個頭文件都定義了一系列供實現使用的明示常量

3.3 printf()

 最初,printf()語句把輸出發送到一個叫作緩衝區(buffer)的中間存儲區域,然後緩衝區中的內容再不斷被髮送到屏幕上。C 標準明確規定了何時把緩衝區中的內容發送到屏幕:當緩衝區滿、遇到換行字符或需要輸入的時候(從緩衝區把數據發送到屏幕或文件被稱爲刷新緩衝區)。

雖然printf()是輸出函數,scanf()是輸入函數,但是它們的工作原理幾乎相同。兩個函數都使用格式字符串和參數列表。 格式字符串包含兩種形式不同的信息:實際要打印的字符;轉換說明。

1) 轉換說明(conversion specification),它們指定了如何把數據轉換成可顯示的形式。

轉換說明把以二進制格式儲存在計算機中的值轉換成一系列字符(字符串)以便於顯示。 

2) 在%和轉換字符之間插入修飾符可修飾基本的轉換說明。

一些簡單的類型處理:

printf()函數中所有float類型的參數(對未使用顯式原型的所有C函數都有效)仍自動轉換成double類型。把size_t定義成系統使用sizeof返回的類型,這被稱爲底層類型(underlying type)。printf()使用z修飾符表示打印相應的類型。同樣,C還定義了ptrdiff_t類型和t修飾符來表示系統使用的兩個地址差值的底層有符號整數類型。

3)  標記

 printf()函數也有一個返回值,它返回打印字符的個數。如果有輸出錯誤,printf()則返回一個負值。在檢查輸出錯誤時可能會用到(如,在寫入文件時很常用)。

有時,printf()語句太長,在屏幕上不方便閱讀。如果空白(空格、製表符、換行符)僅用於分隔不同的部分,C 編譯器會忽略它們。

printf("The printf() function printed %d characters.\n",
rv);
該語句在逗號和rv之間斷行,C編譯器會忽略多餘的空白。

但是,不能在雙引號括起來的字符串中間斷行。如果這樣寫:
printf("The printf() function printed %d
characters.\n", rv);
C編譯器會報錯:字符串常量中有非法字符。
在字符串中,可以使用\n來表示換行字符,但是不能通過按下Enter(或Return)鍵產生實際的換行符。

給字符串斷行有3種方法

方法1:使用多個printf()語句。因爲第1個字符串沒有以\n字符結束。

方法2:用反斜槓(\)和Enter(或Return)鍵組合來斷行。這使得光標移至下一行,而且字符串中不會包含換行符。
printf("Here's another way to print a \
long string.\n");

方法3:ANSI C引入的字符串連接。在兩個用雙引號括起來的字符串之間用空白隔開,C編譯器會把多個字符串看作是一個字符串。
printf("Hello, young "  "lovers" ", wherever you are.");

3.4 scanf()

scanf()函數允許把普通字符放在格式字符串中。除空格字符外的普通字符必須與輸入字符串嚴格匹配。

例如,假設在兩個轉換說明中添加一個逗號:
scanf("%d,%d", &n, &m);
scanf()函數將其解釋成:用戶將輸入一個數字、一個逗號,然後再輸入一個數字。
也就是說,用戶必須像下面這樣進行輸入兩個整數:88,121

除了%c,其他轉換說明都會自動跳過待輸入值前面所有的空白。因此,scanf("%d%d", &n, &m)與scanf("%d %d", &n, &m)的行爲相同。

如果把%c放在格式字符串中的空格前面,scanf()便會跳過空格,從第1個非空白字符開始讀取。也就是說,scanf("%c",  &ch)從輸入中的第1個字符開始讀取,而scanf(" %c", &ch)則從第1個非空白字符開始讀取。

scanf()函數返回成功讀取的項數。如果沒有讀取任何項,且需要讀取一個數字而用戶卻輸入一個非數值字符串,scanf()便返回0。當scanf()檢測到“文件結尾”時,會返回EOF。

printf()的*修飾符。如果你不想預先指定字段寬度,希望通過程序來指定,那麼可以用*修飾符代替字段寬度。

scanf()中*的用法與此不同。把*放在%和轉換字符之間時,會使得scanf()跳過相應的輸出項。

4 運算符與表達式

4.1 運算符

1) 賦值運算符

在C語言中,=並不意味着“相等”,而是一個賦值運算符。=號左側是一個變量名,右側是賦給該變量的值。=號左側的項必須是一個變量名。

左值:它指定一個對象,所以引用內存中的地址。如const

可修改的左值:它指定一個對象且可用在賦值運算符的左側。

右值(rvalue)指的是能賦值給可修改左值的量,且本身不是左值。

整數除法和浮點數除法不同。浮點數除法的結果是浮點數,而整數除法的結果是整數。整數除法結果的小數部分被丟棄,這一過程被稱爲截斷(truncation)。

2) 求模運算符

 求模運算符%用於整數運算。求模運算符給出其左側整數除以右側整數的餘數。

3) 遞增運算符  

遞增運算符將其運算對象遞增1。該運算符以兩種方式出現。第1種方式,++出現在其作用的變量前面,這是前綴模式;第2種方式,++出現在其作用的變量後面,這是後綴模式。

4) 條件運算符

C提供條件表達式(conditional expression)作爲表達if else語句的一種便捷方式,該表達式使用?:條件運算符。該運算符分爲兩部分,需要 3 個運算對象。

條件表達式的通用形式如下:
expression1 ? expression2 : expression3


如果 expression1 爲真(非 0),那麼整個條件表達式的值與 expression2的值相同;如果expression1爲假(0),那麼整個條件表達式的值與expression3的值相同。

5) 邏輯運算符

當且僅當exp1和exp2都爲真時,exp1 && exp2才爲真;
如果exp1或exp2爲真,則exp1 || exp2爲真;
如果exp1爲假,則!exp1爲真;如果exp1爲真,則!exp1爲假。

C 是在美國用標準美式鍵盤開發的語言。C99標準新增了可代替邏輯運算符的拼寫,它們被定義在ios646.h頭文件中。如果在程序中包含該頭文件,便可用and代替&&、or代替||、not代替!。

!運算符的優先級很高,比乘法運算符還高,與遞增運算符的優先級相同,只比圓括號的優先級低。&&運算符的優先級比||運算符高,但是兩者的優先級都比關係運算符低,比賦值運算符高。

除了兩個運算符共享一個運算對象的情況外,C 通常不保證先對複雜表達式中哪部分求值。apples = (5 + 3) * (9 + 6);(能先對錶達式5 + 3求值,也可能先對錶達式9 + 6求值)C 把先計算哪部分的決定權留給編譯器的設計者,以便針對特定系統優化設計。

但是,對於邏輯運算符是個例外,C保證邏輯表達式的求值順序是從左往右。&&和||運算符都是序列點,所以程序在從一個運算對象執行到下一個運算對象之前,所有的副作用都會生效。

6) 優先級

C語言運算符優先級:關係運算符的優先級比算術運算符(包括+和-)低,比賦值運算符高。

優先級

運算符

名稱或含義

使用形式

結合方向

說明

1

[]

數組下標

數組名[常量表達式]

左到右

--

()

圓括號

(表達式)/函數名(形參表)

--

.

成員選擇(對象)

對象.成員名

--

->

成員選擇(指針)

對象指針->成員名

--

2

-

負號運算符

-表達式

右到左

單目運算符

~

按位取反運算符

~表達式

++

自增運算符

++變量名/變量名++

--

自減運算符

--變量名/變量名--

*

取值運算符

*指針變量

&

取地址運算符

&變量名

!

邏輯非運算符

!表達式

(類型)

強制類型轉換

(數據類型)表達式

--

sizeof

長度運算符

sizeof(表達式)

--

3

/

表達式/表達式

左到右

雙目運算符

*

表達式*表達式

%

餘數(取模)

整型表達式%整型表達式

4

+

表達式+表達式

左到右

雙目運算符

-

表達式-表達式

5

<< 

左移

變量<<表達式

左到右

雙目運算符

>> 

右移

變量>>表達式

6

大於

表達式>表達式

左到右

雙目運算符

>=

大於等於

表達式>=表達式

小於

表達式<表達式

<=

小於等於

表達式<=表達式

7

==

等於

表達式==表達式

左到右

雙目運算符

!=

不等於

表達式!= 表達式

8

&

按位與

表達式&表達式

左到右

雙目運算符

9

^

按位異或

表達式^表達式

左到右

雙目運算符

10

|

按位或

表達式|表達式

左到右

雙目運算符

11

&&

邏輯與

表達式&&表達式

左到右

雙目運算符

12

||

邏輯或

表達式||表達式

左到右

雙目運算符

13

?:

條件運算符

表達式1?

表達式2: 表達式3

右到左

三目運算符

14

=

賦值運算符

變量=表達式

右到左

--

/=

除後賦值

變量/=表達式

--

*=

乘後賦值

變量*=表達式

--

%=

取模後賦值

變量%=表達式

--

+=

加後賦值

變量+=表達式

--

-=

減後賦值

變量-=表達式

--

<<=

左移後賦值

變量<<=表達式

--

>>=

右移後賦值

變量>>=表達式

--

&=

按位與後賦值

變量&=表達式

--

^=

按位異或後賦值

變量^=表達式

--

|=

按位或後賦值

變量|=表達式

--

15

逗號運算符

表達式,表達式,…

左到右

--

4.2 表達式

表達式(expression)由運算符和運算對象組成,最簡單的表達式是一個單獨的運算對象。運算對象可以是常量、變量或二者的組合。

語句(statement)是C程序的基本構建塊。一條語句相當於一條完整的計算機指令。在C中,大部分語句都以分號結尾。

複合語句(compound statement)是用花括號括起來的一條或多條語句,複合語句也稱爲塊(block)。

注意,聲明不是表達式語句。也就是說,如果刪除聲明後面的分號,剩下的部分不是一個表達式,也沒有值。

術語(副作用和序列點):

1)副作用是對數據對象或文件的修改。給出表達式states = 50,C會對其求值得50。對該表達式求值的副作用是把變量states的值改爲50;調用 printf()函數時,它顯示的信息其實是副作用。

2)序列點(sequence point)是程序執行的點,在該點上,所有的副作用都在進入下一步之前發生。在 C語言中,語句中的分號標記了一個序列點。

5 控制語句

5.1  While循環

1)表達式:

while循環的通用形式如下:
while ( expression )
statement

statement部分可以是以分號結尾的簡單語句,也可以是用花括號括起來的複合語句。expression是值之間的比較,可以使用任何表達式。

2)真值:

在C語言中,一直用int類型的變量表示真/假值。C99專門針對這種類型的變量新增了_Bool類型。_Bool類型的變量只能儲存1(真)或0(假)。如果把其他非零數值賦給_Bool類型的變量,該變量會被設置爲1。這反映了C把所有的非零值都視爲真。

C99提供了stdbool.h頭文件,該頭文件讓bool成爲_Bool的別名,而且還把true和false分別定義爲1和0的符號常量。包含該頭文件後,寫出的代碼可以與C++兼容,因爲C++把bool、true和false定義爲關鍵字。

3)循環類型

不確定循環:在測試表達式爲假之前,預先不知道要執行多少次循環。while (scanf("%ld", &num) == 1)

計數循環:這類循環在執行循環之前就知道要重複執行多少次。

      在創建一個重複執行固定次數的循環中涉及了3個行爲:
           1.必須初始化計數器;
           2.計數器與有限的值作比較;
           3.每次循環時遞增計數器。

5.2 for

關鍵字for後面的圓括號中有3個表達式,分別用兩個分號隔開。第1個表達式給計數器賦初值,第2個表達式表示計數器的範圍,第3個表達式更新計數器。

for圓括號中的表達式也叫做控制表達式,它們都是完整表達式,所以每個表達式的副作用(如,遞增變量)都發生在對下一個表達式求值之前。

1)逗號運算符:

逗號運算符擴展了for循環的靈活性,for (ounces = 1, cost = FIRST_OZ; ounces <= 16;ounces++,cost += NEXT_OZ)

逗號運算符有兩個其他性質。首先,它保證了被它分隔的表達式從左往右求值;其次,整個逗號表達式的值是右側項的值。

5.3 do while

while循環和for循環都是入口條件循環,即在循環的每次迭代之前檢查測試條件,所以有可能根本不執行循環體中的內容。C語言還有出口條件循環(exit-condition loop),即在循環的每次迭代之後檢查測試條件,這保證了至少執行循環體中的內容一次。這種循環被稱爲 do while循環。

do
statement
while ( expression );

statement可以是一條簡單語句或複合語句。注意,do  while循環以分號結尾。

嵌套循環(nested loop)指在一個循環內包含另一個循環。嵌套循環常用於按行和列顯示數據,也就是說,一個循環處理一行中的所有列,另一個循環處理所有的行。

5.4 IF

if語句被稱爲分支語句(branching statement)或選擇語句(selection statement).

if ( expression )
    statement

如果對expression求值爲真(非0),則執行statement;否則,跳過statement。與while循環一樣,statement可以是一條簡單語句或複合語句。 

if ( expression )
    statement1
else
    statement2

如果要在if和else之間執行多條語句,必須用花括號把這些語句括起來成爲一個塊。 

1) getchar與putchar

getchar()和 putchar()每次只處理一個字符,這種方法很適合計算機。而且,這是絕大多數文本(即,普通文字)處理程序所用的核心方法。

getchar()函數不帶任何參數,它從輸入隊列中返回下一個字符。它們通常是預處理宏,而不是真正的函數

ch = getchar();
該語句與下面的語句效果相同:
scanf("%c", &ch);

 while ((ch = getchar()) != '\n'),把兩個行爲合併成一個表達式。

while (ch = getchar() != '\n'),!=運算符的優先級比=高,所以先對錶達式getchar() != '\n'求值。由於這是關係表達式,所以其值不是1就是0(真或假)。然後,把該值賦給ch。省略圓括號意味着賦給ch的值是0或1,而不是 getchar()的返回值。

2)ctype.h字符函數

C 有一系列專門處理字符的函數,這些函數接受一個字符作爲參數,如果該字符屬於某特殊的類別,就返回一個非零值(真);否則,返回0(假)。

5.5 循環輔助

1)continue

3種循環都可以使用continue語句。執行到該語句時,會跳過本次迭代的剩餘部分,並開始下一輪迭代。如果continue語句在嵌套循環內,則只會影響包含該語句的內層循環。

2)break

程序執行到循環中的break語句時,會終止包含它的循環,並繼續執行下一階段。

 

 

 

 

在for循環中的break和continue的情況不同,執行完break語句後會直接執行循環後面的第1條語句,連更新部分也跳過。嵌套循環內層的break只會讓程序跳出包含它的當前循環,要跳出外層循環還需要一個break。

5.6 多重選擇

break語句可用於循環和switch語句中,但是continue只能用於循環中。如果switch語句在一個循環中,continue便可作爲switch語句的一部分。

程序根據expression的值跳轉至相應的case標籤處。然後,執行剩下的所有語句,除非執行到break語句進行重定向。

expression和case標籤都必須是整數值(包括char類型),標籤必須是常量或完全由常量組成的表達式。如果沒有case標籤與expression的值匹配,控制則轉至標有default的語句(如果有的話);否則,將轉至執行緊跟在switch語句後面的語句。

switch ( expression )
{
case label1 : statement1//使用break跳出switch
case label2 : statement2
default  : statement3
}
可以有多個標籤語句,default語句可選。

1)只讀首字母

while (getchar() != '\n')
continue;    /* 跳過輸入行的其餘部分 */

 循環從輸入中讀取字符,包括按下Enter鍵產生的換行符。注意,函數的返回值並沒有賦給ch,以上代碼所做的只是讀取並丟棄字符。

2)何時使用switch?何時使用if  else?

如果是根據浮點類型的變量或表達式來選擇,就無法使用 switch。如果根據變量在某範圍內決定程序流的去向,使用 switch 就很麻煩,這種情況用if就很方便:
if (integer < 1000 && integer > 2)

但是,如果使用switch,程序通常運行快一些,生成的代碼少一些。

5.7 goto

早期版本的BASIC和FORTRAN所依賴的goto語句,在C中仍然可用。但是C和其他兩種語言不同,沒有goto語句C程序也能運行良好。(最好不用)

goto語句有兩部分:goto和標籤名。標籤的命名遵循變量命名規則,如下所示:
goto part2;
要讓這條語句正常工作,函數還必須包含另一條標爲part2的語句,該語句以標籤名後緊跟一個冒號開始:
part2: printf("Refined analysis:\n");

 實際上,break和continue是goto的特殊形式。使用break和 continue 的好處是:其名稱已經表明它們的用法,而且這些語句不使用標籤,所以不用擔心把標籤放錯位置導致的危險。

6 I/O

6.1 緩衝區

緩衝分爲兩類:完全緩衝I/O和行緩衝I/O。

完全緩衝輸入指的是當緩衝區被填滿時才刷新緩衝區(內容被髮送至目的地),通常出現在文件輸入中。緩衝區的大小取決於系統,常見的大小是 512 字節和 4096字節。

行緩衝I/O指的是在出現換行符時刷新緩衝區。鍵盤輸入通常是行緩衝輸入,所以在按下Enter鍵後才刷新緩衝區。

ANSI C和後續的C標準都規定輸入是緩衝的。

1)getchar()與 scanf()

 getchar()讀取每個字符,包括空格、製表符和換行符;而 scanf()在讀取數字時則會跳過空格、製表符和換行符。不能把它們混用。

while ((ch = getchar()) != '\n')

{
scanf("%d %d", &rows, &cols);
}

 要解決這個問題,程序要跳過一輪輸入結束與下一輪輸入開始之間的所有換行符或空格。

while ((ch = getchar()) != '\n')
{
if (scanf("%d %d", &rows, &cols) != 2)
break;
while (getchar() != '\n')
continue;
}

6.2 結束輸入

1)文件結束符

文件(file)是存儲器中儲存信息的區域。不同的系統儲存文件的方式不同,如果使用標準 I/O 包,就不用考慮這些差異。因此,可以用 if (ch ==)檢查換行符。即使系統實際用的是回車符和換行符的組合來標記行末尾,I/O函數會在兩種表示法之間相互轉換。

從概念上看,C程序處理的是流而不是直接處理文件。

檢測文件結尾的一種方法是,在文件末尾放一個特殊的字符標記文件結尾。CP/M、IBM-DOS和MS-DOS的文本文件曾經用過這種方法。內嵌的Ctrl+Z字符。(現代的文本文件不一定有嵌入的Ctrl+Z,但是如果有,該操作系統會將其視爲一個文件結尾標記。)

另一種方法是儲存文件大小的信息。MS-DOS 及新版DOS,Linux使用這種方法處理二進制文件,因爲用這種方法可以在文件中儲存所有的字符,包括Ctrl+Z。

在C語言中,用getchar()讀取文件檢測到文件結尾時將返回一個特殊的值,即EOF(end  offile的縮寫)。scanf()函數檢測到文件結尾時也返回EOF。通常,  EOF定義在stdio.h文件中:#define EOF (-1)

while ((ch = getchar()) != EOF)  getchar()函數實際返回值的類型是int,所以它可以讀取EOF字符。

在Linux中按下Ctrl+D會傳輸文件結尾信號;在PC中,要按下Ctrl+Z。

2)流

流(stream)是一個實際輸入或輸出映射的理想化數據流。

這意味着不同屬性和不同種類的輸入,由屬性更統一的流來表示。於是,打開文件的過程就是把流與文件相關聯,而且讀寫都通過流來完成。

C把輸入和輸出設備視爲存儲設備上的普通文件,尤其是把鍵盤和顯示設備視爲每個C程序自動打開的文件。stdin流表示鍵盤輸入,stdout流表示屏幕輸出。getchar()、putchar()、printf()和scanf()函數都是標準I/O包的成員,處理這兩個流。

3)重定向

重定向輸入讓程序使用文件而不是鍵盤來輸入,重定向輸出讓程序輸出至文件而不是屏幕。

echo_eof < words
<符號是UNIX和DOS/Windows的重定向運算符。該運算符使words文件與stdin流相關聯,把文件中的內容導入echo_eof程序。

現在,假設你希望製作一份mywords文件的副本,並命名爲savewords。只需輸入以下命令即可:
echo_eof < mywords > savewords
下面的命令也起作用,因爲命令與重定向運算符的順序無關:
echo_eof > savewords < mywords
注意:在一條命令中,輸入文件名和輸出文件名不能相同。

重定向運算符連接一個可執行程序(包括標準操作系統命令)和一個數據文件,不能用於連接一個數據文件和另一個數據文件,也不能用於連接一個程序和另一個程序。

6.3 輸入驗證

is 28 12.4
在我們眼中,這就像是一個由字符、整數和浮點數組成的字符串。但是對  C程序而言,這是一個字節流。第1個字節是字母i的字符編碼,第2個字節是字母s的字符編碼,第3個字節是空格字符的字符編碼,第4個字節是數字2的字符編碼,等等。

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