林銳C C 高質量程式設計指南之二

2022-05-17 10:58:06 字數 4525 閱讀 5026

只是記了一部分我認為比較難理解的,或常用的,部分掌握的就沒有寫。

【規則 7-2-1】用 malloc 或 new 申請記憶體之後,應該立即檢查指標值是否為 null。 防止使用指標值為 null 的記憶體。 

【規則 7-2-2】不要忘記為陣列和動態記憶體賦初值。防止將未被初始化的記憶體作為右 值使用。

【規則 7-2-3】避免陣列或指標的下標越界,特別要當心發生「多 1」或者「少 1」 操作。 

【規則 7-2-4】動態記憶體的申請與釋放必須配對,防止記憶體洩漏。 

【規則 7-2-5】用 free 或 delete 釋放了記憶體之後,立即將指標設定為 null,防止產 生「野指標」

以字串為例比較指標與陣列的特性。

示例 7-3-1 中,字元陣列 a 的容量是 6 個字元,其內容為 hello\0。a 的內容可以改變, 如 a[0]= 『x』。

指標 p 指向常量字串「world」(位於靜態儲存區,內容為 world\0),常 量字串的內容是不可以被修改的。

從語法上看,編譯器並不覺得語句 p[0]= 『x』有什麼 不妥,但是該語句企圖修改常量字串的內容而導致執行錯誤

char a = "

hello";

a[0] = 'x'

;cout

<< a <

char *p = "

world

"; //

注意 p 指向常量字串

p[0] = '

x'; //

編譯器不能發現該錯誤

cout << p << endl;

內容複製與比較 

計算記憶體容量:

char a = "

hello world";

char *p =a;

cout

<< sizeof(a) << endl; //

12 位元組

cout<< sizeof(p) << endl; //

4 位元組

void func(char a[100]) 

7.4指標引數是如何傳遞記憶體的

看下面的幾個例子, 好多筆試題遇見過這幾個例子

例一:

void getmemory(char *p)

void test(void

)請問執行 test 函式會有什麼樣的結果?

答:程式崩潰。

因為 getmemory 並不能傳遞動態記憶體,

test 函式中的 str 一直都是 null。

strcpy(str,"

hello world

");將使程式崩潰。

例二:

void getmemory(char **p, int

num)

void test(void

)請問執行 test 函式會有什麼樣的結果?

答: (1

)能夠輸出 hello

(2)記憶體洩漏 (沒有加free函式)

例三:

char *getmemory(void

)void test(void

)請問執行 test 函式會有什麼樣的結果?

答:可能是亂碼。

因為 getmemory 返回的是指向「棧記憶體」 的指標,

該指標的位址不是 null,但其原 現的內容已經被清除,新內容不可知

例四:

void test(void

)}請問執行 test 函式會有什麼樣的結果?

答:篡改動態記憶體區的內容,後果難以預料,非常危險。

因為free(str);之後,str 成為野指標, if(str != null)語句不起作用。

7.6:動態分配的記憶體會自動釋放嗎?

void getmemory(char *p)

我們發現指標有一些「似是而非」的特徵:

(1)指標消亡了,並不表示它所指的記憶體會被自動釋放。

(2)記憶體被釋放了,並不表示指標會消亡或者成了 null 指標。

7.7野指標

「野指標」不是 null 指標,是指向「垃圾」記憶體的指標

造成野指標出現的原因:

(1)指標變數沒有被初始化。例如:char *p;  這個就是野指標,應該修改為char *p = null;

(2)指標 p 被 free 或者 delete 之後,沒有置為 null

7.11new和delete的使用要點:

int  *p1 = (int *)malloc(sizeof(int) *length);

int *p2 = new

int[length];

delete

p2;如果用

new建立物件陣列,那麼只能使用物件的無引數建構函式。

obj *objects = new obj[100]; //

建立 100 個動態物件

在用 delete

釋放物件陣列時,留意不要丟了符號『』。例如

delete objects; //

正確的用法

delete objects; //

錯誤的用法

後者相當於 delete objects[0],漏掉了另外 99 個物件。

如果 c++程式要呼叫已經被編譯後的 c 函式

假設某個 c 函式的宣告如下:

void foo(int x, int y);

該函式被 c 編譯器編譯後在庫中的名字為_foo,而c++編譯器則會產生像_foo_int_int 之類的名字用來支援函式過載和型別安全連線。

由於編譯後的名字不同,c++程式不能 直接呼叫 c 函式。

c++提供了乙個 c 連線交換指定符號 extern「c」來解決這個問題。

extern

「c」

或者:

extern 「c」

這就告訴 c++編譯譯器,函式 foo 是個 c 連線,應該到庫中找名字_foo 而不是找 _foo_int_int。

c++編譯器開發商已經對 c 標準庫的標頭檔案作了 extern「c」處理,所以我 們可以用#include 直接引用這些標頭檔案

注意並不是兩個函式的名字相同就能構成過載。全域性函式和類的成員函式同名不算 過載,因為函式的作用域不同

呼叫時要加上作用域運算子

class

string

;

建構函式的列表初始化:

如果類存在繼承關係,派生類必須在其初始化表裡呼叫基類的建構函式。

拷貝建構函式和賦值函式,當涉及到預設的含有記憶體操作時,要注意。

。以類 string 的兩個物件 a,b 為例,假設 a.m_data 的內容為「hello」, b.m_data 的內容為「world」。

現將 a 賦給 b,預設賦值函式的「位拷貝」意味著執行 b.m_data = a.m_data。

這將造成三個錯誤:

一是 b.m_data 原有的記憶體沒被釋放,造成記憶體洩露;

二是 b.m_data 和 a.m_data 指向同一塊記憶體,a 或 b 任何一方變動都會影響另一方;

三是 在物件被析構時,m_data 被釋放了兩次。

string類的成員函式實現:

// string 的普通建構函式 

string::string(const

char *str)

else

}

// string 的析構函式

string::~string(void

)

拷貝構造和賦值函式

//

拷貝建構函式

string::string(const string &other)

//賦值函式

string &string::operate =(const string &other)

《高質量程式設計指南 C C語言》 林銳

一 c 檔案結構 c c 程式的標頭檔案以 h 為字尾,c 程式的定義檔案以 c 為字尾,c 程式 的定義檔案通常以 cpp 為字尾 1 標頭檔案結構 h 標頭檔案由三部分內容組成 1 標頭檔案開頭處的版權和版本宣告。2 預處理塊。3 函式和類結構宣告等。為了防止標頭檔案被重複引用,應當用ifnde...

c c 高質量程式設計(林銳)讀書筆記二

標頭檔案由三部分內容組成 1 標頭檔案開頭處的版權和版本宣告 參見示例1 1 2 預處理塊。3 函式和類結構宣告等。假設頭檔名稱為graphics.h,標頭檔案的結構參見示例1 2。規則1 2 1 為了防止標頭檔案被重複引用,應當用ifndef define endif結構產生預處理塊。規則1 2 ...

高質量C C程式設計指南

1.如果引數是指標,且僅作輸入用,則應在型別前加const,以防止該指標在函式體內被意外修改 2.在函式體的 入口處 對引數的有效性進行檢查 在函式體的 出口處 對return語句的正確性和效率進行檢查 3.引用的一些規則如下 1 引用被建立的同時必須被初始化 指標則可以在任何時候被初始化 2 不能...