1. 程式人生 > >《C和指標》第1章 快速入手

《C和指標》第1章 快速入手

1.1.2 預處理指令

#include <stdio.h>
#define MAX_COLS 20
這2行稱為預處理指令(preprocessor directives),因為它們是由前處理器(preprocessor)解釋的。前處理器讀入原始碼,根據預處理指令對其進行修改,然後把修改過的原始碼遞交給編譯器。

int red_colum_number(int columns[], int max);
int rearrange(char *output, char const *input,int n_columns, int const columns[]);
這些宣告稱為函式原型(function prototype)。它們告訴編譯器這些以後將在原始檔中定義的函式的特徵。

1.1.3 main 函式

int main(void){
這構成了main函式定義的起始部分。每個C程式都必須有一個main函式,因為它是程式執行的起點。關鍵字int表示函式返回一個整型值,關鍵字void表示函式不接受任何引數。main函式的函式體包括左花括號和與之相匹配的右花括號之間的任何內容。

在C語言中,資料引數是以引用(reference)形式進行傳遞的,也就是傳址呼叫,而標量和常數則是按值(value)傳遞的。在函式中對標量引數的任何修改都會在函式返回時丟失,因此,被呼叫函式無法修改呼叫函式以傳至形式傳遞給它的引數。然而,當被呼叫函式修改陣列引數的其中一個元素時,呼叫函式所傳遞的陣列就會被實際地修改。

在C程式中,處理字串是常見的任務之一。儘管C語言並不存在“String”資料型別,但在整個語言中,存在一項約定:字串就是一串以NUL位元組結尾的字元。NUL是作為字串終止符,它本身並不被看做是字串的一部分。字串常量(string literal)就是原程式中被雙引號括起來的一串字元。例如,字串常量:
“Hello”
在記憶體中佔據6個位元組空間,按順序分別是H、e、l、l、o和NUL。
表1.1 常用printf格式程式碼

格式 含義
%d 以十進位制形式列印一個整型值
%o 以八進位制形式列印一個整型值
%x 以十六進位制形式列印一個整型值
%g 列印一個浮點值
%c 列印一個字元
%s 列印一個字串
\n 換行

scanf函式的返回值是函式成功轉換並存儲與引數中的值的個數。
**警告:**對於這個函式,首先,由於scanf函式的實現原理,所有標量引數的前面必須加上一個“&”符號。陣列引數前面不需要加上“&”符號(但是,即使你在它前面加上一個“&”也沒有什麼不對)。但是,陣列引數中如果出現了下標引用,也就是說實際引數是陣列的某個特定元素,那麼它的前面也必須加上“&”符號。
**警告:**第二個需要注意的地方是格式程式碼,它與printf函式的格式程式碼頗為相似卻又並不完全相同,所以很容易引起混淆。表1.2粗略列出了一些你可能會在scanf函式中用到的格式程式碼。注意,前5個格式程式碼用於讀取標量值,所以變數引數的前面必須加上“&”符號。使用所有格式程式碼(除了%c之外)時,輸入值之前的空白(空格、製表符、換行符號)會被跳過,值後面的空白表示該值的結束。因此,用%s格式碼輸入字串時,中間不能包含空白。除了表中所列之外,還存在許多格式程式碼,但是這張表裡面的這幾個格式程式碼對於應付我們現在的需求已經足夠了。
表1.2 常用scanf格式碼

格式 含義 變數類
%d 讀取一個整型值 int
%ld 讀取一個長整型值 long
%f 讀取一個實型值(浮點數) float
%lf 讀取一個雙精度實型值 double
%c 讀取一個字元 char
%s 從輸入中讀取一個字串 char型陣列

我們現在可以解釋表示式:
scanf("%d",&columns[num])
格式碼%d表示需要讀取一個整型值。字元是從標準輸入讀取,前導空白將被跳過。然後這些數字被轉換為一個整數,結果儲存於指定的陣列元素中。我們需要在引數前面加上一個“&”符號,因為陣列下標選擇的是一個單一的陣列元素,它是一個標量。

/*
**取得列標號,如果所讀取的數小於0則停止
*/
while(num<max && scanf("%d",&columns[num])==1 && columns[num]>0)
num += 1;
提示:標準並未硬性規定C編譯器對陣列小標的有效性進行檢查,而且絕大多數C編譯器確實也部進行檢查。因此,如果你需要進行陣列下標的有效性檢查,你必須自行編寫程式碼。如果此處不進行num<max這個測試,而且程式所讀取的檔案包含超過20個列標號,那麼多出來的值就會儲存在緊隨陣列之後的記憶體位置,這樣就會破壞原先儲存在這個位置的資料,可能是其他變數,也可以是函式的返回地址,這可能會導致多種結果,程式很可能不會按照你預想的那樣執行。

警告:&&與&(按位與)不同
警告:==與=。因為這兩個都是合法的表示式,所以編譯器無法為你找出這個錯誤。

/*
**確認已經讀取的標號為偶數個
*/
if(num % 2 != 0){
puts(“Last column number is not paired.”);
exit( EXIT_FAILURE );
}
num是否為偶數。%操作符執行整數的除法,但它給出的結果是除法的餘數而不是商。
puts函式是gets函式的輸出版本,它把指定的字串寫到標準輸出並在末尾添上一個換行符。程式接著呼叫exit函式,終止程式的執行,EXIT_FAILURE這個值被返回給作業系統,提示出現了錯誤。

(ch = getchar() ) != EOF
getchar函式從標準輸入讀取一個字元並返回它的值。如果輸入中不再存在任何字元,函式就會返回常量EOF(在stdio.h中定義),用於提示檔案的結尾。

1.4 總結

本章的目的是描述足夠的C語言的基礎知識,使你對C語言有一個整體的印象。
本章的例子程式說明了許多要點。註釋以/開始,以/結束,用於在程式中新增一些描述性的說明。#include預處理指令可以使一個函式庫標頭檔案的內容由編譯器進行處理,#define指令允許你給字面值常量取個符號名。
所有的C程式必須有一個main函式,它是程式執行的起點。函式的標量引數通過傳值的方式進行傳遞,而陣列名引數則具有傳址呼叫的語義。字串是由一串由NUL位元組結尾的字元,並且有一組庫函式以不同的方式專門用於操縱字串。printf函式執行格式化輸出,scanf函式用於格式化輸入,getchar和putchar分別執行非格式化字元的輸入和輸出。if和while語句在C語言中的用途跟它們在其他語言中的用途差不太多。

1.5 警告的總結

1. 在scanf函式的標量引數前未新增&字元。
2. 機械地把printf函式的格式程式碼照搬於scanf函式。
3. 在應該使用&&操作符的地方誤用了&操作符。
4. 誤用=操作符而不是==操作符來測試相等性。

1.6 程式設計提示的總結

1. 使用#include指令避免重複宣告。
2. 使用#define指令給常量值取名。
3. 在#include檔案中放置函式原型。
4. 在使用下標前先檢查它們的值。
5. 在while或if表示式中蘊含賦值操作。
6. 如何編寫一個空迴圈體。
7. 始終要進行檢查,確保陣列不越界。