深入理解C指標之三 指標和函式

2021-09-22 20:35:33 字數 2676 閱讀 5857

原文:

深入理解c指標之三:指標和函式

理解函式和指標的結合使用,需要理解程式棧。大部分現代的塊結構語言,比如c,都用到了程式棧來支援函式的執行。呼叫函式時,會建立函式的棧幀並將其推到程式棧上。函式返回時,其棧幀從程式棧上彈出。

在使用函式時,有兩種情況指標很有用。一種是將指標作為引數傳遞給函式,函式可以修改指標所引用的資料,可以高效的傳遞大塊資料。另一種是宣告函式指標。

程式的棧和堆是c程式的重要執行時元素。程式棧是支援函式執行的記憶體區域,通常和堆共享一塊記憶體區域。通常程式棧在區域的下部,堆在上部。程式棧存放棧幀(stack frame),棧幀有時候也稱為活躍記錄或活躍幀。棧幀存放函式引數和區域性變數。堆則管理動態記憶體。

呼叫函式時,函式的棧幀被推到棧上,棧向上「長出」乙個棧幀。當函式終止時,其棧幀從程式上彈出。棧幀所使用的記憶體不會被清理,但有可能被另乙個棧幀覆蓋。

棧幀由以下幾種元素組成:

* 返回位址。函式完成後要返回的程式內部位址。

* 區域性資料儲存。為區域性變數分配的記憶體。

* 引數儲存。為函式引數分配的記憶體。

* 棧指標和基指標。執行時系統用來管理棧的指標。

棧指標通常指向棧頂部。基指標(幀指標)通常存在並指向棧幀內部的位址,比如返回位址,用來協助訪問棧幀內部的元素。這兩個指標都是執行時系統用來管理程式棧的位址。系統在建立棧幀時,將引數以跟宣告時相反的順序推到幀上,最後推入區域性變數。c把塊語句當做「微型」函式,會在適當的時候將其推入棧和從棧上彈出。

引數和區域性變數的精確位址可能會變化,不過順序一般不變。這一點可以解釋引數和變數分配記憶體的相對順序。將棧幀推到程式棧上時,系統可能會好近記憶體,這種情況稱為棧溢位。要記住每個執行緒通常都有自己的程式棧,乙個或多個執行緒訪問記憶體中的同乙個物件可能會導致衝突。

傳遞引數(包括指標)的時候,傳遞的是它們的值的副本。當涉及大型資料結構時,傳遞引數的指標會更高效。傳遞物件的指標意味著不需要複製物件,但可以通過指標訪問物件。

用指標來傳遞資料的乙個主要原因是函式可以修改資料。如果不希望函式修改資料,可以傳遞指向常量的指標。

從函式返回指標有兩種情況:一種是在函式內部為指標分配記憶體並返回指向記憶體的指標,另一種是函式的呼叫者負責指標記憶體的分配和釋放。從函式返回指標可能存在幾個潛在的問題:

* 返回未初始化的指標。

* 返回指向無效位址的指標。

* 返回區域性變數的指標。

* 返回指標但是沒有釋放記憶體。

設想一下在函式中宣告乙個指標並為該指標分配記憶體,但是在呼叫該函式的時候沒有使用乙個指標變數來接受函式的返回值,因此我們丟失了該分配記憶體的位址,導致記憶體洩露。

區域性資料指標是指當函式返回的時候,(非動態分配的)區域性資料的位址也就無效了。動態分配的記憶體與此不同,存在於堆上,即使函式返回也不受影響。當變數為static,就會在棧幀外部為其分配記憶體,每次呼叫函式都會訪問同一塊記憶體。

在將指標作為引數傳遞時,在使用之前判斷指標是否為null是個好習慣。

重複free乙個指標會引發錯誤,在free之前也應判斷指標是否為null。而且在釋放之後將指標置為null。

傳遞指標將允許我們修改指標指向的資料,如果我們想修改的是指標怎麼辦?當然是傳遞指向指標的指標了。

函式指標是持有函式位址的指標。通過將函式指標作為引數傳遞,使我們避免將事件處理程式硬編碼進函式裡,更加靈活。

void (*foo)();

void是返回型別,foo是指標變數名,後邊的括號裡是引數。

int(*fptr1)(int);

int square(int num)

fptr1 = square;

int n = 5;

printf(" result is %d\n", fptr1(n));

使用函式的名字直接給函式指標賦值,它會把函式的位址賦給指標。也可以對函式名字使用取位址操作符,但是意思是一樣的,在此處上下文中會忽略取位址操作符。可以為函式指標宣告乙個型別定義,通過型別定義來宣告函式指標。

typedef int (*funcptr)(int);

funcptr ptr2;

ptr2 = square;

傳遞函式指標只需把函式指標宣告為引數即可。

funcptr ptr2;

ptr2 = square;

int compute(funcptr squ, int num)

compute(ptr2,n);

返回函式指標需要把函式的返回型別宣告為函式指標。函式指標陣列可以基於某些條件選擇要執行的函式,宣告這種陣列只要把函式指標宣告為陣列的型別即可。

typedef int (*operation)(int,int);

operation operations[128] = ;//使用null來初始化所有陣列元素

我們可以用相等和不等操作符來比較函式指標。不同的函式指標的長度不一定相等。雖然可以將一種函式指標轉換為另一種函式指標,但是應該謹慎使用這種方法,因為執行時系統不會驗證函式指標所用的引數是否正確。不應該把函式指標轉換成void*指標。基本指標用作佔位符,用來交換函式指標的值。一定要確保給函式指標傳遞的引數是正確的,否則會造成不確定行為。

總體來說,函式指標允許應用程式根據不同的條件執行不同的函式,對

於控制應用

程式內的執

行序列很有用。

深入理解C指標之三 指標和函式

原文 深入理解c指標之三 指標和函式 理解函式和指標的結合使用,需要理解程式棧。大部分現代的塊結構語言,比如c,都用到了程式棧來支援函式的執行。呼叫函式時,會建立函式的棧幀並將其推到程式棧上。函式返回時,其棧幀從程式棧上彈出。在使用函式時,有兩種情況指標很有用。一種是將指標作為引數傳遞給函式,函式可...

深入理解 C 指標 七 指標和函式的關係

可以把乙個指標宣告成為乙個指向函式的指標。int fun1 char int int pfun1 char int pfun1 fun1 int a pfun1 abcdefg 7 通過函式指標呼叫函式。可以把指標作為函式的形參。在函式呼叫語句中,可以用指標表示式來作為實參。例十三 int fun ...

深入理解C指標之四 指標和陣列

原文 深入理解c指標之四 指標和陣列 陣列是c內建的基本資料結構,陣列表示法和指標表示法緊密關聯。一種常見的錯誤認識是陣列和指標完全可以互換,儘管陣列名字有時可以當做指標來用,但陣列的名字不是指標。陣列和指標的區別之一就是,儘管陣列的名字可以返回陣列位址,但是名字不能作為賦值操作的目標。陣列是能用索...