🍁博客主頁:江池俊的博客
💫收錄專欄:C語言進階之路
💡代碼倉庫:江池俊的代碼倉庫
🎪我的社區:GeekHub
🎉歡迎大家點贊👍評論📝收藏?
文章目錄
- 一、函數指針
- 代碼1:
- 代碼2:
- 二、函數指針數組
- 什么是函數指針數組?
- 為什么使用函數指針數組?
- 函數指針數組的基本用法
- 三、 指向函數指針數組的指針
- 指向函數指針數組的指針是什么?
- 為什么使用指向函數指針數組的指針?
- 總結
一、函數指針
在C語言中,函數是一等公民,可以像其他變量一樣被傳遞和使用。而函數指針就是指向函數的指針變量,可以用來調用函數。本文將介紹函數指針的定義、使用方法以及注意事項。
函數指針的定義格式為:
返回值類型 (*指針變量名)(參數列表);
其中,返回值類型表示函數的返回值類型,指針變量名是指向函數的指針變量的名稱,參數列表表示函數的參數類型和數量。
例如,定義一個指向返回值為int、參數為兩個int類型的函數的指針:
int (*pAdd)(int, int);
這個指針可以指向任何返回值為int、參數為兩個int類型的函數。
先看一段代碼:
#include <stdio.h>
void test()
{printf("hehe\n");
}int main()
{printf("%p\n", test);printf("%p\n", &test);return 0;
}
輸出結果:
輸出的是兩個地址,這兩個地址是 test 函數的地址。
那我們的函數的地址要想保存起來,怎么保存?
下面我們看代碼:
void test()
{printf("hehe\n");
}
//下面pfun1和pfun2哪個有能力存放test函數的地址?
void (*pfun1)();
void *pfun2();
首先,能給存儲地址,就要求pfun1或者pfun2是指針,那哪個是指針?
答案是:
pfun1可以存放。pfun1先和*結合,說明pfun1是指針,指針指向的是一個函數,指向的函數無參數,返回值類型為void。
閱讀兩段有趣的代碼:
//代碼1
(*(void (*)())0)();
//代碼2
void (*signal(int , void(*)(int)))(int);
這兩段代碼都涉及了函數指針的用法,讓我們逐一來解釋它們:
代碼1:
(*(void (*)())0)();
這段代碼是一個函數指針調用的例子。讓我們逐步分解它:
void (*)()
表示一個函數指針類型,它指向一個不接受任何參數(void
),并且返回類型為void
的函數。
(void (*)())0
這里的(void (*)())
實際上是一個強制轉化的操作,它是將0強制轉化為函數指針類型,即是將函數指針初始化為一個地址為 0 的空指針,也就是一個無效的函數指針。
(*(void (*)())0)();
則是將這個無效的函數指針進行了間接調用,實際上是試圖調用地址為 0 的函數,這通常會導致程序崩潰(因為操作系統不允許在地址 0 處執行代碼,會觸發段錯誤)。
這段代碼在 C 語言中屬于未定義行為,不應該在實際代碼中使用,因為它可能導致程序的崩潰或其他不可預測的行為。
代碼2:
void (*signal(int, void(*)(int)))(int);
//我們也可以將它簡化為以下這種形式:
typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);
這段代碼涉及的是 C 語言中的信號處理函數
signal
的聲明。讓我們逐步解釋它:
void(*)(int)
表示一個函數指針類型,它指向一個接受一個int
參數并返回void
的函數。
signal
是一個函數,它接受兩個參數:一個int
參數和一個函數指針參數,然后返回一個與上述函數指針類型匹配的函數指針。
所以整個代碼聲明的含義是:
signal
是一個函數,它接受一個int
參數和一個函數指針參數,返回一個函數指針,該函數指針指向一個接受一個int
參數并返回void
的函數,這個函數通常用于處理信號。
這段代碼通常用于在 C 語言中設置信號處理函數,以便在程序接收到特定信號時執行特定的操作。
請注意,這里只是聲明了
signal
函數的原型,實際使用時需要根據具體情況編寫函數體。
注
:推薦《C陷阱和缺陷》
這本書中提及這兩個代碼。
二、函數指針數組
函數指針數組是 C 語言中一個強大且常用的工具,用于存儲指向不同函數的指針,允許根據需要調用特定的函數。在本文中,我們將深入介紹函數指針數組的概念、用途和實例,幫助你理解并充分利用這一重要的 C 語言特性。
什么是函數指針數組?
數組是一個存放相同類型數據的存儲空間,那我們已經學習了指針數組
比如:
int *arr[10];
//數組的每個元素是int*
那要把函數的地址存到一個數組中,那這個數組就叫函數指針數組,那函數指針的數組如何定義呢?
int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];
答案是:parr1
parr1
先和[]
結合,說明parr1
是數組,數組的內容是什么呢?
是int (*)()
類型的函數指針。
函數指針數組的用途:轉移表
總之,函數指針數組實際上是一個數組,其元素都是指向函數的指針。這使得我們可以將不同的函數存儲在數組中,并通過索引來調用特定的函數。這種靈活性使得函數指針數組在編寫菜單驅動程序、狀態機、回調機制等方面非常有用。以下是一個簡單的示例:
#include <stdio.h>void func1()
{printf("調用 func1函數\n");
}void func2()
{printf("調用 func2函數\n");
}int main()
{void (*funcPtrArray[2])() = {func1, func2};//函數指針數組funcPtrArrayfuncPtrArray[0](); //通過函數指針數組調用 func1funcPtrArray[1](); //通過函數指針數組調用 func2return 0;
}
為什么使用函數指針數組?
函數指針數組在以下情況下非常有用:
-
菜單驅動程序: 當需要實現一個用戶界面,允許用戶從菜單中選擇不同的操作時,函數指針數組可以用來存儲每個操作的處理函數。
-
狀態機: 在狀態機的實現中,可以使用函數指針數組來存儲每個狀態的處理函數,從而實現狀態轉換時的操作。
-
回調機制: 當你需要在某個事件發生時調用不同的函數,比如事件處理、信號處理等,函數指針數組提供了一種簡潔的方式。
-
動態選擇算法: 如果你有多個算法實現,但在運行時決定使用哪一個算法,函數指針數組可以幫助你實現動態選擇算法。
函數指針數組的基本用法
讓我們通過一個簡單的例子來演示函數指針數組的基本用法:實現一個簡單的計算器,允許用戶選擇不同的操作。
例子:(計算機)
#include <stdio.h>int add(int a, int b)
{return a + b;
}int subtract(int a, int b)
{return a - b;
}int multiply(int a, int b)
{return a * b;
}int division(int a, int b)
{return a / b;
}int main()
{int x, y;int input = 1;int restult = 0;// 定義函數指針數組,存儲不同的操作函數//轉移表int(*operation[])(int x, int y) = { 0,add,subtract,multiply,division };while (input){printf("\n*************************\n");printf(" 1:add 2:subtract \n");printf(" 3:multiply 4:division \n");printf("*************************\n");printf("請選擇:");scanf("%d", &input);if ((input <= 4 && input >= 1)){printf("輸入操作數:");scanf("%d %d", &x, &y);restult = (*operation[input])(x, y);// 調用選定的函數printf("restult = %d\n", restult);}elseprintf("輸入有誤,請重新輸入\n");}return 0;
}
在上述示例中,我們定義了一個函數指針數組 operation
,其中的元素分別指向 add
、subtract
、 multiply
和division
函數。用戶可以根據選擇來執行不同的操作。
三、 指向函數指針數組的指針
指向函數指針數組的指針是什么?
指向函數指針數組的指針是一個指針
,指針指向一個的數組
,數組元素都是函數指針
。這種指針提供了對函數指針數組的更高級別的訪問方式,使我們能夠更靈活地處理函數指針數組。以下是一個示例:
#include <stdio.h>void func1()
{printf("調用 func1函數\n");
}void func2()
{printf("調用 func2函數\n");
}int main()
{void (*funcPtrArray[2])() = {func1, func2};//函數指針的數組funcPtrArrayvoid (*(*ptrToFuncPtrArray))() = funcPtrArray;//指向函數指針數組funcPtrArray的指針ptrToFuncPtrArrayptrToFuncPtrArray[0](); // 通過指向函數指針數組的指針調用 func1ptrToFuncPtrArray[1](); // 通過指向函數指針數組的指針調用 func2return 0;
}
為什么使用指向函數指針數組的指針?
指向函數指針數組的指針可能在日常編程中不常見,但在某些情況下非常有用。以下是一些使用情況:
-
函數指針數組的參數傳遞: 通過傳遞指向函數指針數組的指針作為參數,可以避免復制整個數組,從而提高效率。
-
動態函數調用: 使用指向函數指針數組的指針,可以在運行時根據條件選擇不同的函數進行調用。
-
代碼模塊化: 當函數指針數組較大或需要在多個函數之間共享時,使用指向函數指針數組的指針可以提高代碼的模塊化性。
-
函數指針數組的排序: 可以使用指向函數指針數組的指針來執行對函數指針數組的排序操作,以實現按照某種規則調用函數。
總結
- 在本篇博客中,我們深入探討了 C語言中的三個重要概念:函數指針、函數指針數組和指向函數指針數組的指針。這些概念雖然可能聽起來有些復雜,但它們為我們在C編程中提供了更大的靈活性和功能。
- 函數指針允許我們將函數作為數據,傳遞給其他函數或存儲在數據結構中。通過使用函數指針,我們可以實現更動態和可配置的程序設計,同時避免代碼的重復。
- 函數指針數組進一步擴展了這種靈活性,允許我們將多個函數指針組織在一個數組中,以便在運行時根據需要選擇和調用不同的函數。這在構建可插拔的模塊和實現動態行為時特別有用。
- 最后,我們介紹了指向函數指針數組的指針,這為我們提供了一種更高級的訪問方式,使得處理函數指針數組變得更加優雅。它可以應用于參數傳遞、動態函數調用、代碼模塊化以及對函數指針數組的排序等各種場景,從而增強了程序的模塊性、可維護性和性能。
🔥今天的分享就到這里, 如果覺得博主的文章還不錯的話, 請👍三連支持一下博主哦🤞