1. 程式人生 > >2017-2018-1 20179215 《從問題到程序》第五章

2017-2018-1 20179215 《從問題到程序》第五章

efi mrr 堅持 stdlib.h clu 無符號 全局 文字信息 選擇

《從問題到程序》第五章讀書筆記

?這章主要從五個方面介紹,分別為數值類型、函數和標準庫函數、函數定義和程序的函數分解、c程序的結構與變量及預處理。主要是對函數整體的理解以及常用的規範進行說明。

一、數值類型

1.實數類型和整數類型

技術分享圖片

?除了實數類型之外的數值類型都是整數類型。C語言將字符類型也看作整數類型可以作為整數參加運算。各種整數類型都分為帶符號與無符號的兩種,帶符號類型表示一定範圍內的正數和負數,符號類型的值都不小於0。

2.字符類型

?在一般 C 語言系統裏,一個字符占一個字節,其中存著字符的編碼。字符類型主要用於存儲文字信息和輸入輸出。如果把字符當作整數參加運算,所用的就是字符的編碼(這是一個整數)。

技術分享圖片

3.整數類型

技術分享圖片

(1)無符號整數類型的一個特點是算術運算以對應類型的表示範圍為模進行。當計算結果超出類型的表示範圍時,以取模後的余數作為計算結果。 假定 unsigned 用 16 位表示,表示範圍是 0~65535。 如果計算結果超出這個範圍,就以得到的結果除以 65536 的余數作為結果。例如 234+65500 的結果將是 198。其他無符號類型的情況也一樣。

(2)由於類型問題,計算中可能出現隱含的類型轉換動作。 C 語言規定, 當各種小整數類型(short、 unsigned short 類型,各種 char 類型)的數據出現在表達式之中,計算之前先將它們轉換為 int 類型的值後再參與運算,這一過程稱為整數提升。在基本類型相同時, C 語言認為無符號類型是比同樣有符號類型更大的類型。舉例說,如果要做下面計算:2365U + 18764,首先要從整型值 18764 轉換生成一個無符號整數的對應值,然後用這個新值參與計算。

(3)基本數據類型的選擇

技術分享圖片

二、函數和標準庫函數

?C 標準庫函數完成一些最常用的基本功能, 包括基本輸入和輸出、 文件操作、 存儲管理,以及其他一些常用功能函數, 如數學函數、 數據值的類型轉換函數等。

1.字符分類函數

技術分享圖片

應當在程序前部用#include 命令包含系統頭文件 ctype.h。在這個頭文件裏還說明了兩個字母大小寫轉換函數:

技術分享圖片

2.隨機數生成函數

要使用標準庫的隨機數生成功能, 程序前部應包含系統文件 stdlib.h, 這個文件裏描述了與隨機數生成有關的函數:

int rand(void)

這是一個無參函數,每次調用將得到一個新隨機整數, 其值在 0 和系統定義的符號常量RAND_ MAX 之間。不同系統裏的 RAND_ MAX 可能不同,一般系統中用 32767。

void srand(unsigned seed)

這個函數用參數 seed 的值重新設置種子值, 即為生成下一個隨機數而保存的一個整數值(由它出發遞推,取得下一個隨機數)。默認的初始種子值是 1。

三、函數定義和程序的函數分解

技術分享圖片

(1)函數調用

技術分享圖片

(2)函數封裝

技術分享圖片

(3)參數傳遞

技術分享圖片

(4)過時的函數定義形式與原型形式

1.在寫無參函數的原型時,參數表必須寫成(void),不能簡單寫成(),因為這一形式將被看著是過時原型形式。

2.我們應堅持的正確原則是: 1)如果使用庫函數,那麽就必須在源文件前部用#include命令包含必要的頭文件(xxx.h 文件)。 2) 對所有未能在使用前給出定義的函數(無論它是定義在本文件後面, 還是在其他源文件裏),都應給出正確完整的原型說明。 3)把原型說明寫在源文件最前面(不要寫在函數內部),以使函數的定義點和所有使用點都能“看到”同一個原型說明。如果堅持了這些原則,就能避免函數調用與定義不一致的錯誤.

四、C 程序結構與變量

1.外部變量

?外部定義、 外部說明是指寫在源程序文件表層的定義和說明(不在函數體內)。函數定義是一種外部定義, 寫在外層(不在函數體內) 的原型說明是一種外部說明。 一個外部定義或者說明總從它出現的位置開始起作用,其作用範圍一直延續到文件結束。

技術分享圖片

技術分享圖片

2.靜態局部變量

?書中舉了個小問題實際提出了對另一種變量的需求,這種變量的作用域應是局部的,定義在函數體裏,從而保證信息的隱蔽性,避免其他函數無意的越權訪問; 而其存在期應是全局的,因此可以跨越函數的不同調用,在兩次調用間傳遞信息。 此外,這種變量的初始化只應進行一次, 使變量值能在函數的不同調用間保持。 C 語言的靜態局部變量就是這樣的。 靜態局部變量的定義位置與其他局部變量一樣,另用關鍵字 static 指明其特殊性。靜態局部變量的性質就是上面三條:局部作用域、全程存在期、一次初始化。

例如一個函數它每調用一次就輸出一個空格,每調用到第 10 次時輸出一個換行。 將這個函數命名為 format,代碼如下:

void format(void) {
static int m = 0;
if (++m == 10) {
putchar(‘\n‘);
m = 0;
}
else putchar(‘ ‘);
}

五、預處理

?預處理命令以獨立的預處理命令行的形式出現。 # 符號是特殊引導符號,如果源程序裏某行的第一個非空格符號是 #, 那麽這行就是預處理命令行。

技術分享圖片

1.宏定義

?由 #define 開始的行稱為宏定義命令行。 宏定義行有兩種形式:

技術分享圖片

技術分享圖片

2.宏定義與函數調用的區別

(1) 宏定義和調用並不考慮類型問題。

(2)在執行方面, 宏調用將在程序加工中被在現場展開, 不會形成運行中的調用動作

3.帶參宏有幾個值得特別註意的問題。

(1)有些宏展開後會引起對參數的多次計算,而從宏調用的形式上完全看不到這種情況。 例如上面定義的 min,展開之後總會有一個參數表達式被計算兩次,有時這種情況會引來奇怪後果。請看下面調用:

z = min(n++, m++);

展開的結果是:

z = ((n++) < (m++) ? (n++) : (m++))

無論變量 n 和 m 在語句執行前的情況如何,總會有一個變量做了兩次增量操作。這個情況在程序正文中完全看不到,很可能成為程序裏難以發現的錯誤。

(2)人們通常都在帶參宏的替代正文中寫許多括號,把各參數的出現都括起,也把整個段括起,以防展開後由於運算符的優先級而引起問題。假設定義了下面的求平方宏:

#define square(x) x * x

表面上看它完全正確,但在特定環境下它卻可能出問題。考慮下面調用:

z = square(x + y);

宏展開後得到的是:

z = x + y * x + y;

這顯然不可能是寫程序的人所希望的。

3.條件編譯命令

技術分享圖片

註:

一個簡單猜數遊戲,根據書中的講解親自實現了一下:

技術分享圖片

2017-2018-1 20179215 《從問題到程序》第五章