函式指標

函式在記憶體區塊text segment,有記憶體位址。
使用函式指標,指向函式的記憶體位址。

定義函式指標

傳回值類型 (*指標名)(參數類型1, 參數類型2, 參數類型3 ....)

以下函式指標名是pf,傳回值類型是int,參數有2個,參數類型都為int。

1
int (* pf)(int,int);

函式名就是記憶體位址

函式指標指向函式名的記憶體位址。

傳回值類型 (*指標名)(參數類型 ...) = 函式名

以下max()函式也符合傳回值類型是int,參數數量為2,參數類型為int。

1
2
3
4
5
6
int max(int x, int y) {
  if (x > y) 
    return x;
  else 
    return y;
}

把max函式名(記憶體位址)指派給pf函式指標。

1
int (* pf)(int,int) = max;

函式指標存放的位址

下圖中,Stack區域有一個pf變數,本身記憶體位址是0x1000,存放的記憶體位址是0x2000。

img

使用程式碼印出pf指標變數,印出pf與*pf,二者的值都是存放的記憶體位址0x2000。

1
2
3
4
5
6
7
8
9
10
11
int max(int x, int y) {
  if (x > y)
    return x;
  else
    return y;
}
int main() {
  int (* pf)(int,int) = max;
  printf("pf addr= %p, &pf addr= %p, * pf addr = %p\n",pf, &pf, * pf);
  return 0;
}
pf addr= 0x2000, &pf addr= 0x1000, * pf addr = 0x2000

函式指標呼叫函式

pf指標存放的是max()函式記憶體位址。

方式1

1
int ret = pf(x,y);

完整程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
int max(int x, int y) {
  if (x > y)
    return x;
  else
    return y;
}
int main() {
  int (* pf)(int,int) = max;
  int a = 10, b = 20;
  int ret = pf(a, b);
  cout << ret << endl;
  return 0;
}
20

方式2

pf指標存放的是max()函式記憶體位址。函式記憶體位址使用*取值運算子,把max()函式整個從記憶體中取出來。

1
int ret = (* pf)(x,y);

完整程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
int max(int x, int y) {
  if (x > y)
    return x;
  else
    return y;
}
int main() {
  int (* pf)(int,int) = max;
  int a = 10, b = 20;
  int ret = (* pf)(a, b);
  cout << ret << endl;
  return 0;
}
20

運算子優先順序

函式指標正確寫法,回傳值類型為int整數。

1
int (* pf)(int,int);

錯誤寫法,沒有使用括號:

1
int * pf(int,int);

回傳值類型為「int指標」,函式名為pf。

1
int* pf(int,int);

所以使用圓括號包住指標名(* pf),相當重要,沒有圓括號,傳回值類型就變成指標。

函式指標作為函式參數

建立一個大小為10的陣列,裡面的值都是亂數。

產生亂數的getRand()函式

1
2
3
int getRand() {
  return rand();
}

根據getRand()函式,定義函式指標。
傳回值是int,沒有參數,void代表沒有參數。

1
int(* pf)(void);

初始化陣列函式initArr(),把函式指標作為函式參數傳入。

1
void initArr(int arr[], int size, int(* pf)(void));

由函式指標去呼叫getRand()函式。

1
2
3
4
5
void initArr(int arr[], int size, int(* pf)(void)) {
  for (int i = 0; i < size; i++) {
    arr[i] = pf();  // 呼叫getRand()
  }
}

完整程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int getRand() {
  return rand();
}
void initArr(int arr[], int size, int(* pf)(void)) {
  for (int i = 0; i < size; i++) {
    arr[i] = pf();
  }
}
int main() {
  int arr[10] = {0};
  int len = sizeof(arr) / sizeof(int);
  initArr(arr, len, getRand);
  for (int i = 0; i < len; i++) {
    cout << arr[i] << ", ";
  }
  cout << endl;
  return 0;
}
16807, 282475249, 1622650073, 984943658, 1144108930, 470211272, 101027544, 1457850878, 1458777923, 2007237709, 

以下為舊文章

函式資料型態

函式資料型態是由函式的傳回值資料型態/參數資料型態/參數的數量所組成。

如果多個函式的傳回值資料型態/參數資料型態/參數的數量都一樣,代表這些函式是同一個資料型態。

以下三個函式都是相同資料型態,傳回值資料型態/參數資料型態/參數的數量都一樣。

  • int func1(int code, string msg);
  • int func2(int age, string name);
  • int func3(int userId, string userName);

以下三個函式都是相同資料型態,傳回值資料型態/參數資料型態/參數的數量都一樣。

  • bool func4(string param1);
  • bool func5(string msg);
  • bool func6(string name);

以下二個函式不是相同資料型態。

  • int func1(int code, string msg);
  • bool func4(string param1);

以下三個函式的函式資料型態為 int (*pf1)(int,string),其中pf1為函式指標的變數名,可以為任意名稱,名稱前面要有星號*,要用括號()包起來,int為傳回值資料型態,(int,string)為函式參數資料型態與參數數量。

  • int func1(int code, string msg);
  • int func2(int age, string name);
  • int func3(int userId, string userName);

以下三個函式的函式資料型態為 bool (*pf2)(string),其中pf2為函式指標的變數名,可以為任意名稱。

  • bool func4(string param1);
  • bool func5(string msg);
  • bool func6(string name);

宣告函式指標

1
2
3
4
5
6
7
8
9
10
int func1(int code, string msg) {
  cout << "Err code = " << code << ", msg = " << msg << endl;
  return code;
}
int main() {
  int (*pf1)(int,string); //宣告函式指標變數pf1
  pf1 = func1; //將func1函式設給pf1函式指標變數
  pf1(404, "Page not found."); //使用函式指標pf1呼叫函式
  return 0;
}

第1行,宣告函式。

第6行,宣告函式指標變數pf1。

第7行,將func1函式設給pf1函式指標變數。

第8行,使用函式指標pf1呼叫函式。

執行結果
Err code = 404, msg = Page not found.

typedef函式指標類型別名

語法

typedef 傳回值(*類型別名)(參數1,參數2,參數3 ...);

將前一個宣告函式指標的程式碼

1
int (*pf1)(int,string); 

改成下方的程式碼,前面添加typedef,並把pf1改成Func1,Func1可以為任意名字,並放在程式的開頭。

1
typedef int (*Func1)(int,string);

修改完的程式碼如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
using namespace std;
//宣告Func1類型別名
typedef int (*Func1)(int,string);
int func1(int code, string msg) {
  cout << "Err code = " << code << ", msg = " << msg << endl;
  return code;
}
int main() {
  //宣告指標變數pf1為Func1類型
  Func1 pf1; //宣告函式指標變數pf1
  pf1 = func1; //函式指標變數pf1設定函式
  pf1(404, "Page not found.");//使用函式指標pf1呼叫函式
  return 0;
}
執行結果
Err code = 404, msg = Page not found.

函式參數是函式指標

宣告函式print404Msg(),第一個參數為函式指標,函式的資料型態是傳回值為int,函式指標名為pf1,2個參數資料型態分別為int跟string。

1
2
3
void print404Msg(int (*pf1)(int,string), string msg) {
  pf1(404, msg);
}

第1行,宣告函式print404Msg(),第一個參數為函式指標,第二個參數為string資料型態。

第2行,使用函式指標pf1呼叫函式,並把404整數與msg的參數傳進函式。

完整程式

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
using namespace std;
void print404Msg(int (*pf1)(int,string), string msg) {
  pf1(404, msg);
}
int func1(int code, string msg) {
  cout << "Err code = " << code << ", msg = " << msg << endl;
  return code;
}
int main() {
  print404Msg(func1, "Page not Found");
  return 0;
}
執行結果
Err code = 404, msg = Page not Found

函式參數是typedef函式指標類型別名

也可以使用typedef函式指標類型另取別名。

1
2
3
4
5
6
//宣告Func1類型別名
typedef int (*Func1)(int,string);
//第一個參數資料型態為Func1
void print404Msg(Func1 pf1, string msg) {
  pf1(404, msg);
}

完整程式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
using namespace std;
typedef int (*Func1)(int,string);
void print404Msg(Func1 pf1, string msg) {
  pf1(404, msg);
}
int func1(int code, string msg) {
  cout << "Err code = " << code << ", msg = " << msg << endl;
  return code;
}
int main() {
  print404Msg(func1, "Page not Found");
  return 0;
}

函式指標應用

自訂二個函式指標類型別名

1
2
3
4
5
//宣告類型別名
//傳回值為void,別名為Success,參數資料型態char*指標
typedef void (*Success)(char*);
//傳回值為void,別名為Failure,參數類型分別為int,char*指標
typedef void (*Failure)(int, char*);
1
2
3
4
5
6
void httpOk(char* msg) {
  printf("成功,結果:%s\n", msg);
}
void httpFailure(int code, char* msg) {
  printf("失敗%d,原因:%s\n", code, msg);
}

第1行,宣告函式,傳回值與參數資料型態都符合函式指標類型別名Success

第4行,宣告函式,傳回值與參數資料型態都符合函式指標類型別名Failure

1
2
3
4
5
6
7
8
9
10
11
12
void http(int res, Success success, Failure failure) {
  if(res == 1) {
    success("取得資料成功");
  } else {
    failure(505,"網路連線有問題");
  }
}
int main() {
  http(1,httpOk,httpFailure);
  http(0,httpOk,httpFailure);
  return 0;
}

第1行,宣告函式,第1個參數資料型態int,第2個參數函式指標類型別名Success,第3個參數函式指標類型別名Failure。

第3行,使用函式指標Success呼叫函式。

第5行,使用函式指標Failure呼叫函式,並傳入參數。

第9行,呼叫http函式,並把函式傳回值參數與自訂Success函式資料型態一樣的httpOk函式傳入。

第10行,呼叫http函式,並把函式傳回值參數與自訂Failure函式資料型態一樣的httpFailure函式傳入。

完整程式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
using namespace std;
//函式指標類型別名
typedef void (*Success)(char*);
typedef void (*Failure)(int, char*);
void httpOk(char* msg) {
  printf("成功,結果:%s\n", msg);
}
void httpFailure(int code, char* msg) {
  printf("失敗%d,原因:%s\n", code, msg);
}
void http(int res, Success success, Failure failure) {
  if(res == 1) {
    success("取得資料成功");
  } else {
    failure(505,"網路連線有問題");
  }
}
int main() {
  http(1,httpOk,httpFailure);
  http(0,httpOk,httpFailure);
  return 0;
}
執行結果
成功,結果:取得資料成功
失敗505,原因:網路連線有問題

results matching ""

    No results matching ""