[C語言] - function returning a pointer

函式除了可以回傳一般的變數類型外,也可以回傳指標,包含了變數指標、陣列指標、函式指標等等

函式回傳變數指標

函式可以回傳一個變數的位址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int * fun_rt_para(int a)
{
static int b = 0;

printf("input value : %d\n", a);
b += a;

return &b;
}

int main(void)
{
printf("output value : %d\n\n", *fun_rt_para(1));
printf("output value : %d\n\n", *fun_rt_para(3));
printf("output value : %d\n\n", *fun_rt_para(5));

return 0;
}

以上程式碼顯示結果如下,函式將b的位址傳回,這裡要注意的是b必須是全域變數或是static變數,因為local變數的生命週期只有在此函式內(local變數一般放在stack或CPU register),出了這個函式再透過指標位址取值,有可能取到錯誤的值

函式回傳陣列指標

函式也可以回傳陣列指標

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int * fun_rt_arr()
{
static int a[3] = {1, 2, 3};

return a;
}

int main(void)
{
printf("output value : %d\n", *(fun_rt_arr + 0));
printf("output value : %d\n", *(fun_rt_arr + 1));
printf("output value : %d\n", *(fun_rt_arr + 2));

return 0;
}

顯示結果如下,不過使用此方式缺點為caller無法從function name得知陣列大小,caller有可能讀取到邊界外的值,甚至破壞到陣列外的資料,此方法在安全性來講並不妥當

若想讓caller看function就可以知道陣列大小,可以使用以下方式,輸出結果和上面的例子相同,但事實上回傳的type不完全相同

上例回傳的type為int *p,指向int的指標
下例回傳的type為int (*p)[3]指向int[3]的指標

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int (* fun_rt_arr())[3]
{
static int a[3] = {1, 2, 3};

return &a;
}

int main(void)
{
printf("output value : %d\n", (*fun_rt_arr())[0]);
printf("output value : %d\n", (*fun_rt_arr())[1]));
printf("output value : %d\n", (*fun_rt_arr())[2]);

return 0;
}

函式回傳函式指標

函式也可以回傳另一個函式的指標

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

int f1(int b)
{
printf("Enter f1, value = %d\n", b);

return ++b;
}

int f2(int b)
{
printf("Enter f2, value = %d\n", b);

return b+=2;
}

int (* fun_rt_fun_ptr(int a))(int b)
{
if (a == 1)
return f1;
else
return f2;
}

int main(void)
{
printf("output value : %d\n\n", (fun_rt_fun_ptr(1))(10));
printf("output value : %d\n\n", (fun_rt_fun_ptr(2))(20));

return 0;
}

細部解析函式的宣告,從最內部的fun_rt_fun_ptr開始

int (* fun_rt_fun_ptr(int a))(int b)
  1. (向右看)fun_rt_fun_ptr為輸入int a的函式
  2. (向左看)fun_rt_fun_ptr回傳一指標
  3. (向右看)此指標為函式指標,且為輸入int b的函式
  4. (向左看)此回傳的函式指標回傳值為int

註:對於較複雜的宣告可參考以下文章 http://www.codeproject.com/Articles/7042/How-to-interpret-complex-C-C-declarations

指標函式回傳函式指標

指標函式回傳函式指標和函式回傳函式指標,原理沒什麼太大的不同,只是宣告上會更為複雜些

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
int f1(int b)
{
printf("Enter f1, value = %d\n", b);

return ++b;
}

int f2(int b)
{
printf("Enter f2, value = %d\n", b);

return b+=2;
}

int f3(int b)
{
printf("Enter f3, value = %d\n", b);

return ++b;
}

int f4(int b)
{
printf("Enter f4, value = %d\n", b);

return b+=2;
}

int (* fun_rt_fun_ptr1(int a))(int b)
{
if (a == 1)
return f1;
else
return f2;
}

int (* fun_rt_fun_ptr2(int a))(int b)
{
if (a == 10)
return f3;
else
return f4;
}

int main(void)
{
int (* (*func_ptr[])(int))(int) = {fun_rt_fun_ptr1, fun_rt_fun_ptr2};

printf("output value : %d\n\n", func_ptr[0](1)(10));
printf("output value : %d\n\n", func_ptr[0](2)(20));
printf("output value : %d\n\n", func_ptr[1](10)(30));
printf("output value : %d\n\n", func_ptr[1](20)(40));

return 0;
}

函式宣告的部分和函式回傳函式指標沒什麼不同,這也是非常正常的,因為所謂的指標函式並不是指函式本身是指標,而是以指標的方式串接此函式,因此要注意的地方是

int (* (*func_ptr[])(int))(int) = {fun_rt_fun_ptr1, fun_rt_fun_ptr2};

除了宣告的型別必須是回傳函式指標的函式之外,還必須有一個儲存函式指標的陣列

printf("output value : %d\n\n", func_ptr[0](1)(10));
printf("output value : %d\n\n", func_ptr[0](2)(20));
printf("output value : %d\n\n", func_ptr[1](10)(30));
printf("output value : %d\n\n", func_ptr[1](20)(40));

以上的行為依序如下

  1. call fun_rt_fun_ptr1帶入參數1,因參數為1進入f1帶入參數10,回傳值為11
  2. call fun_rt_fun_ptr1帶入參數2,因參數為2進入f2帶入參數20,回傳值為22
  3. call fun_rt_fun_ptr2帶入參數10,因參數為10進入f3帶入參數30,回傳值為31
  4. call fun_rt_fun_ptr2帶入參數20,因參數為20進入f4帶入參數40,回傳值為42