C C 學習筆記 基礎知識10

2021-06-23 09:27:13 字數 3482 閱讀 7653

1棧、堆和靜態區

一般來說,可以簡單的理解為記憶體分為三個部分:靜態區,棧,堆。

其實堆疊就是棧,而不是堆。堆的英文是heap;棧的英文是stack,也翻譯為堆疊。堆和棧都有自己的特性。

記憶體也是這樣,記憶體的三個部分,不是所有的東西都能存進去的。

靜態區:儲存自動全域性變數和static變數(包括static全域性和區域性變數)。靜態區的內容在總個程式的生命週期內都存在,由編譯器在編譯的時候分配。

棧:儲存區域性變數。棧上的內容只在函式的範圍內存在,當函式執行結束,這些內容也會自動被銷毀。其特點是效率高,但空間大小有限。

堆:由malloc系列函式或new操作符分配的記憶體。其生命週期由free或delete決定。在沒有釋放之前一直存在,直到程式結束。其特點是使用靈活,空間比較大,但容易出錯。

2 其他

(1)指標變數

定義指標變數的同時最好初始化為null,用完指標之後也將指標變數的值設定為null。也就是說除了在使用時,別的時間都把指標「栓」到0位址處

不管什麼時候,我們使用指標之前一定要確保指標是有效的。一般在函式入口處使用assert(null != p)對引數進行校驗。

在非引數的地方使用if(null != p)來校驗。但這都有乙個要求,即p在定義的同時被初始化為null了

(2)assert巨集

assert是乙個巨集,而不是函式,包含在assert.h標頭檔案中。如果其後面括號裡的值為假,則程式終止執行,並提示出錯;如果後面括號裡的值為真,則繼續執行後面的**。

這個巨集只在debug版本上起作用,而在release版本被編譯器完全優化掉,這樣就不會影響**的效能

使用assert巨集的地方在release版本裡面確實沒有了這些校驗。但是我們要知道,assert巨集只是幫助我們除錯**用的,它的一切作用就是讓我們盡可能的在除錯函式的時候

把錯誤排除掉,而不是等到release之後。它本身並沒有除錯功能。再有一點就是,引數出現錯誤並非本函式有問題,而是呼叫者傳過來的實參有問題。

assert巨集可以幫助我們定位錯誤,而不是排除錯誤。

char *p1 = 「abcdefg」;

char *p2 = (char *)malloc(sizeof(char)*strlen(p1)); //錯誤,字串常量的結束標誌『\0』

char *p2 = (char *)malloc(sizeof(char)*strlen(p1)+1*sizeof(char)); //正確

strcpy(p2,p1);

只有字串常量才有結束標誌符。比如下面這種寫法就沒有結束標誌符了:

char a[7] = ;

(3)變數初始化

在定義乙個變數時,第一件事就是初始化

//可以把它初始化為乙個有效的值,比如:

int i = 10;

char *p = (char *)malloc(sizeof(char));

//但是往往這個時候我們還不確定這個變數的初值,這樣的話可以初始化為0或null。

int i = 0;

char *p = null;

//如果定義的是陣列的話,可以這樣初始化:

int a[10] = ;

//或者用memset函式來初始化為0:

memset(a,0,sizeof(a));

//memset函式有三個引數,第乙個是要被設定的記憶體起始位址;第二個引數是要被設定的值;第三個引數是要被設定的記憶體大小,單位為byte

(4)記憶體洩漏

會產生****存就是堆上的記憶體(這裡不討論資源或控制代碼等洩漏情況),也就是說由malloc系列函式或new操作符分配的記憶體。如果用完之後沒有及時free或delete,這塊記憶體就無法釋放,直到整個程式終止。

malloc是乙個函式,專門用來從堆上分配記憶體。使用malloc函式需要幾個要求:

記憶體分配給誰?分配多大記憶體?是否還有足夠記憶體分配?記憶體的將用來儲存什麼格式的資料,即記憶體用來做什麼?分配好的內存在**?

如果這五點都確定,那記憶體就能分配。下面先看malloc函式的原型:

(void *)malloc(int size)

malloc函式的返回值是乙個void型別的指標,引數為int型別資料,即申請分配的記憶體大小,單位是byte。記憶體分配成功之後,malloc函式返回這塊記憶體的首位址。你需要乙個指

針來接收這個位址。但是由於函式的返回值是void *型別的,所以必須強制轉換成你所接收的型別。也就是說,這塊記憶體將要用來儲存什麼型別的資料。比如:

char *p = (char *)malloc(100);

在堆上分配了100個位元組記憶體,返回這塊記憶體的首位址,把位址強制轉換成char *型別後賦給char *型別的指標變數p。同時告訴我們這塊記憶體將用來儲存char型別的資料。也就是說你只能通過指標變數p來操作這塊記憶體。這塊記憶體本身並沒有名字,對它的訪問是匿名訪問。

上面就是使用malloc函式成功分配一塊記憶體的過程。但是,每次你都能分配成功嗎?

不一定。使用malloc函式同樣要注意這點:如果所申請的記憶體塊大於目前堆上剩餘記憶體塊(整塊),則記憶體分配會失敗,函式返回null。注意這裡說的「堆上剩餘記憶體塊」不是所有剩餘記憶體塊之和,因為malloc函式申請的是連續的一塊記憶體。既然malloc函式申請記憶體有不成功的可能,那我們在使用指向這塊記憶體的指標時,必須用if(null!=p)語句來驗證記憶體確實分配成功了。

申請0位元組記憶體,函式並不返回null,而是返回乙個正常的記憶體位址。但是你卻無法使用這塊大小為0的記憶體。

對於這一點一定要小心,因為這時候if(null!=p)語句校驗將不起作用

free函式只有乙個引數,就是所要釋放的記憶體塊的首位址

作用:斬斷指標變數與這塊記憶體的關係

既然使用free函式之後指標變數p本身儲存的位址並沒有改變,那我們就需要重新把p的值變為null:

p = null;

char *p = (char *) malloc(100);

strcpy(p, 「hello」);

free(p); /* p 所指的記憶體被釋放,但是p所指的位址仍然不變*/

?if (null != p)

//釋放完塊記憶體之後,沒有把指標置null,這個指標就成為了「野指標」,也有書叫「懸垂指標」。

//這是很危險的,而且也是經常出錯的地方。所以一定要記住一條:free完之後,一定要給指標置null。

記憶體已經被釋放了,但是繼續通過指標來使用

第一種:就是上面所說的,free(p)之後,繼續通過p指標來訪問記憶體。解決的辦法就是給p置null。

第二種:函式返回棧記憶體。這是初學者最容易犯的錯誤。比如在函式內部定義了乙個陣列,卻用return語句返回指向該陣列的指標。解決的辦法就是弄明白棧上變數的生命週期。

第三種:記憶體使用太複雜,弄不清到底哪塊記憶體被釋放,哪塊沒有被釋放。解決的辦法是重新設計程式,改善物件之間的呼叫關係。

C C 學習筆記 基礎知識3

1 和 和 有什麼區別 1 和 對運算元進行求值運算,和 只是判斷邏輯關係。2 和 在在判斷左側運算元就能確定結果的情況下就不再對右側運算元求值。注意 在程式設計的時候有些時候將 或 替換成 或 沒有出錯,但是其邏輯是錯誤的,可能會導致不可預想的後果 比如當兩個運算元乙個是 1 另乙個是 2 時 2...

C C 學習筆記 基礎知識6

1複雜表示式與序列點 序列點是乙個時間點 在整個表示式全部計算完畢之後或在 或逗號運算子處,或在函式呼叫之前 此刻塵埃落定,所有的 都已確保結束.ansi iso c 標準這樣描述 在上乙個和下乙個序列點之間,乙個物件所儲存的值至多只能被表示式的計算修改一次。而且前乙個值只能用於決定將要儲存的值。i...

C C 學習筆記 基礎知識7

1字元和字串 在 c 語言中字元用它們的字符集值對應的小整數表示。因此,你不需要任何轉換函式 如有你有字元,你就有它的值。數字字元和它們對應的 0 9 的數字之間相互轉換時,加上或減去常數 0 也就是說,0 的字元值。字串用字元陣列表示 通常你操作的是字元陣列的第乙個字元的指標,c語言從來不會把陣列...