1. 程式人生 > 其它 >指標型函式與函式型指標 -2021.08.04

指標型函式與函式型指標 -2021.08.04

指標型函式與函式型指標 -2021.08.04

儲存區域劃分

  1. 棧 Stack 可讀可寫,儲存程式執行時函式或程式碼中的區域性變數(非static變數),編譯器自動分配和釋放。棧屬於動態記憶體分配,它的生存期為:程式碼塊執行就分配空間,程式碼塊結束,就自動回收空間
  2. 堆 Heap 可讀可寫,儲存程式執行時被程式設計師動態分配的記憶體段,它的大小並不固定,可動態擴張或縮減。堆屬於動態記憶體分配,主要通過malloc, calloc, realloc, free等方法來管理
  3. 程式碼段 Code Segment / Text Segment 一般只讀,儲存程式程式碼指令,還有可能包含一些只讀的常數變數,例如字串常量等。程式碼段的空間大小在程式碼執行前就已經確定,其空間分配從可執行檔案中載入而來,屬於靜態記憶體分配。
  4. 資料段 Data Segment 可讀可寫,儲存初始化的(全域性和區域性範圍的)靜態變數,資料段的空間分配從可執行檔案中載入而來,其空間大小執行時不會改變,屬於靜態記憶體分配。
  5. ROdata Segment (Read-Only data Segment) 只讀,儲存只讀資料,比如字串常量、靜態常量、const修飾的變數、字面量以及預編譯指令(#define等)定義的常量
  6. BSS Segement (Block Started by Symbol) 可讀可寫,屬於靜態記憶體分配,儲存全域性範圍(file scope)中未初始化的變數和常量,以及區域性範圍中未初始化的靜態(static)變數,還包括被顯示地初始化為0的全域性變數和靜態變數

附圖Linux x86-64 run-time memory image

函式 Function

在C語言中,函式名也稱為函式的指標,函式名錶示的就是函式體的首地址

指標型函式 Pointer Function

定義

指標型函式(a function that returns a pointer),其本質是個函式,但是這種函式的返回值為指標型別的資料,即記憶體中的某一個地址,簡單來說需要在函式體中返回一個指標變數。指標型函式宣告或定義的時候需要遵循的規則為:函式返回值型別* 函式名(函式形參列表)

注意:在返回指標變數時,該指標變數不能夠指向函式體內部的區域性變數,因為函式體內的區域性變數是存放在棧Stack當中的,函式在執行結束後,其記憶體會被編譯器自動釋放,該指標變數指向的內容將會變得不確定。舉個例子:

#include <stdio.h>

void correct_get_str(char* text)
{
	sprintf(text, "%s", "correct");
}

char* wrong_get_str()
{
	char text[100] = { '\0' };
	sprintf(text, "%s", "wrong");
	return text;
}

int main()
{
	printf("============Wrong Get Str===========\n");
	char* wrong = wrong_get_str();
	printf("%s\n", wrong);
	printf("============Correct Get Str===========\n");
	char text[100] = {'\0'};
	correct_get_str(text);
	printf("%s\n", text);
	return 0;
}

執行結果:

============Wrong Get Str===========
燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙燙(?燙燙燙燙燙燙燙燙t??
============Correct Get Str===========
correct

錯誤的用法是在函式中定義一個指標變數,並初始化其內容,在用指標型函式返回該指標變數,而正確的用法是在函式的形參中宣告一個指標變數,並在函式體內部改變,呼叫該函式時,傳入在函式外部定義的指標變數實參,或者是在函式體內使用malloc控制堆記憶體,但是需要注意記憶體的分配與釋放

函式型指標 Function Pointer

定義

函式型指標(a pointer-to-function),本質是一個指標,其形式與其他資料型別的指標類似,但是這種指標是指向某一個函式,其功能是通過該指標呼叫所指向的函式。函式型指標宣告或定義的時候需要遵循的規則為:函式返回值型別 (*指標名)(函式形參列表),其中

  • 函式返回值型別: 需要與待指向函式的返回值型別一致
  • (*指標名): 前後必須有括號,否則*屬於函式返回值型別,則宣告變為函式返回值型別* 函式名(函式形參列表),是一個指標型函式
  • 函式形參列表: 必須寫出函式形參的型別,需要與待指向函式的形參型別一致,可以選擇不寫出形參的名稱

函式型指標的使用方式,第1步指向函式:指標名 = 函式名,第2步使用指標名呼叫函式:指標名(實參列表)或者(*指標名)(實參列表)

指標型函式與函式型指標的使用例項

#include <stdio.h>

//普通函式
int print(int i)
{
	printf("%d\n", i);
	return i;
}

//指標型函式,返回值為指標型別的資料
int* pointer_function(int* i)
{
	printf("%d\n", *i);
	return i;
}

//函式型指標,指向函式的指標,與被指向函式的返回值型別和引數完全一致
//注意(*function_pointer1)的括號必須要加上
int (*function_pointer1)(int);

//引數位函式型指標,使用方法與function_pointer1類似
int function_pointer2(int (*print)(int))
{
	print(2);
	return 2;
}

//引數位函式型指標,此處不寫*也可以,引數型別與function_pointer3相同
int function_pointer3(int print(int))
{
	print(3);
	return 3;
}

int main()
{
	printf("指標型函式\n");
	printf("Pointer Function\n");
	int a = 1;
	int* pointer = pointer_function(&a);
	printf("================================\n");
	printf("函式型指標\n");
	printf("Function Pointer 1\n");
	function_pointer1 = print;
	//呼叫所指方法的兩種方式
	function_pointer1(1);
	(*function_pointer1)(1);
	printf("Function Pointer 2\n");
	function_pointer2(print);
	printf("Function Pointer 3\n");
	function_pointer3(print);
	return 0;
}

執行結果:

指標型函式
Pointer Function
1
================================
函式型指標
Function Pointer 1
1
1
Function Pointer 2
2
Function Pointer 3
3

在VS2019中通過Debug,檢視指標型函式與函式型指標的型別:

  • pointer_funtion 指標型函式,其型別為int* (int *),返回值是一個int指標
  • funtion_pointer1 函式型指標,其型別為int (*)(int),指向函式printprint的型別為int (int)
  • funtion_pointer2 形參int (*print)(int)為函式型指標,其本身型別為int (int (*)(int)),形參型別為 int (*)(int)
  • funtion_pointer3 形參int print(int)也是函式型指標,與funtion_pointer2完全一致,此處(*print)print沒有區別,因為函式名就是函式的指標