網站首頁 編程語言 正文
一、函數的指針
首先,函數名代表函數的起始地址,調用函數時,程序會從函數名獲取到函數起始地址,并從該地址起執行函數中的代碼,函數名就是函數的指針,所以我們可以定義一個指向函數的指針變量,用來存放函數的起始地址,這樣一來,就可以通過該變量來調用其所指向的函數。
二、指向函數的指針變量
定義指向函數的指針變量
返回值類型(* 指針變量名)(形參類型列表);
例如:int(*p)(int, int);
,這行代碼定義了一個可以指向返回值為整型且有兩個整型形參函數的指針變量p,符合返回值為整型且有兩個整型形參的函數都可以將其地址(即其函數名)賦給p。
使用指向函數的指針變量
在使用指向函數的指針變量時,只需要將函數名賦給指向函數的指針變量即可,因為函數名就是該函數的入口地址。
由于指向函數的指針變量保存了函數的地址,則該指針變量就指向了對應的函數。例如,求最大值的函數命名為max
,如果將其函數名賦給指向函數的指針變量p
(即p = max
)后,則p
就指向了max
函數,并且可以通過(*p)(a, b);
的方式來調用max
函數,因為指針變量p
保存了max
函數的地址,那么*p
就是max
。需要注意的是,其中*p
前的*
可以省略,故也可以寫成p(a, b);
三、調用函數的兩種方式
引例:自定義max
函數,求整數a
和b
中的較大者并返回給主調函數,不考慮兩數相等的情況通過函數名調用函數
#include <stdio.h> int max(int, int); // max函數的函數聲明 int main() { int a, b; printf("請輸入兩個整數:"); scanf("%d%d", &a, &b); printf("兩數中的較大者的值為%d\n", max(a, b)); return 0; } int max(int a, int b) { if (a > b) return a; else return b; }
通過指向函數的指針變量調用函數
#include <stdio.h> int max(int, int); // max函數的函數聲明 int main() { int a, b; int(*p)(int, int); // 定義指向函數的指針p p = max; // p指向max函數 printf("請輸入兩個整數:"); scanf("%d%d", &a, &b); printf("兩數中的較大者的值為%d\n", (*p)(a, b)); // (*p)(a, b) 也可寫為 p(a, b) return 0; } int max(int a, int b) { if (a > b) return a; else return b; }
四、指向函數的指針的作用
看到這里有人可能會問,既然函數名就可以調用函數,為什么還要弄個奇奇怪怪的指針?這難道不是多此一舉嘛?難倒是為了裝13?不管怎樣,使用指向函數的指針來調用函數肯定不是為了裝13,主要的原因是:用函數名調用函數時比較死板,只能調用所指定的一個函數,而通過指針變量調用函數會比較靈活,可以根據不同的情況調用不同的函數。以下面的程序為例: 輸入兩個整數,然后讓用戶選擇1或2,選1則調用max
函數求出兩個整數的較大者并將其輸出,選2則調用min
函數求出兩個整數的較小者并將其輸出,不考慮兩數相等的情況
#include <stdio.h> int max(int, int); // max函數的函數聲明 int min(int, int); // min函數的函數聲明 int main() { int a, b, c, n; int (*p)(int, int); // 定義指向函數的指針p p = NULL; // 先將p賦為空 printf("請輸入兩個整數:"); scanf("%d%d", &a, &b); printf("輸入1獲取兩個數中的較大者,輸入2獲取兩個數中的較小者,請輸入:"); scanf("%d", &n); if (n == 1) p = max; // p指向max函數 else if (n == 2) p = min; // p指向min函數 c = p(a, b); // 調用p所指向的函數 if (n == 1) printf("兩個數中的較大者為:%d\n", c); else printf("兩個數中的較小者為:%d\n", c); return 0; } int max(int a, int b) { if (a > b) return a; else return b; } int min(int a, int b) { if (a < b) return a; else return b; }
五、用指向函數的指針作函數參數(重點)
指向函數的指針變量的一個重要用途是把函數的入口地址作為實參傳遞給其他函數。以下面的程序為例:
有兩個整數a
和b
,由用戶輸入1,2,3來決定進行什么操作。輸入1則求出a
和b
中的較大者,輸入2則求出a
和b
中的較小者,輸入3則求出a
與b
之和,不考慮兩個數相等的情況
#include <stdio.h> int fun(int, int, int (*p)(int, int)); int max(int, int); int min(int, int); int sum(int, int); int main() { int a = 34, b = -21, n; printf("輸入1獲得兩數中的較大者,輸入2獲得兩數中的較小者,輸入3獲得兩個數的和,請輸入:"); scanf("%d", &n); if (n == 1) printf("兩數中的較大者為%d\n", fun(a, b, max)); // 向fun函數中傳參時,只需要傳入兩個整數或整型變量以及想要在fun函數內執行的函數的函數名即可 // 函數名會傳遞給對應的形參指針變量 else if (n == 2) printf("兩數中的較小者為%d\n", fun(a, b, min)); else if (n == 3) printf("兩個數的和為%d\n", fun(a, b, sum)); return 0; } // fun函數的作用是獲取最終結果 int fun(int x, int y, int (*p)(int, int)) { int result; result = p(x, y); // 用result接收最終結果,不管執行max,min,sum中的哪個函數,fun函數內部代碼都不用改變 return result; } int max(int x, int y) { if (x > y) return x; else return y; } int min(int x, int y) { if (x < y) return x; else return y; } int sum(int x, int y) { return x + y; }
從上面的程序中可以清晰地看出,不管調用max
,min
,sum
中的哪個函數,fun
函數均沒有任何變化,在fun
函數內部的result
只用來獲取結果并將結果返回,但不去判斷到底要通過哪個函數來計算這一結果,主調函數向其傳入哪個函數,其內部就執行哪個函數。max
,min
,sum
函數用來計算,fun
函數用來獲取結果,這體現出了整個程序的模塊化。
六、為什么要將指向函數的指針變量作為函數的形參(重點)
舉一個例子,我們在學習數組的過程中,想要把數組中的所有元素輸出,通常會接觸一個新詞,遍歷。其實遍歷的含義并不是將一個結構中的元素輸出的過程,然而我在初學時便認為遍歷等同于輸出,這是我在初學時對遍歷這個詞不準確的理解,我相信也一定有人跟我一樣這樣認為。其實遍歷指的是依次訪問某種結構中的所有元素,至于對這些元素怎么操作,由程序員自己決定,比如,你想輸出所有的元素,那就可以調用輸出函數將每次獲取到的元素輸出;你想將所有元素的值翻倍,那就調用對應的翻倍函數將每次獲取到的元素翻倍。但是這樣一來,遍歷函數的功能就變得十分單一,只能進行一種操作,要么是遍歷并輸出,要么是遍歷并翻倍,如果在一個程序中,開始想要遍歷并翻倍,后又想要遍歷并輸出,就只能定義兩個函數來實現,但是我們發現不管對元素怎么操作,訪問每個元素的代碼都是相同,并且只要想對結構中的每個元素進行操作,首先要做的就是訪問每個元素。但是如果為了輸出而定義一個先遍歷后輸出的函數,為了將每個元素的值翻倍而定義一個先遍歷后翻倍的函數,這樣遍歷元素的代碼就是重復的。那要怎么辦呢?既然遍歷的操作是重復的,那我們就定義一個專門的遍歷函數,該函數只用來訪問元素,再定義其它多個操作數據的函數,至于我們對遍歷后的數據執行什么樣的操作,我們只需要將對應的操作函數通過遍歷函數的形參接收過來,這樣就可以實現在遍歷函數中根據不同情況執行不同操作的目的,如此一來既體現出了程序設計的結構化與模塊化,又減少了編程時的代碼量。
原文鏈接:https://blog.csdn.net/weixin_65334260/article/details/125732248
相關推薦
- 2022-07-14 C#中的并發集合Concurrent類_C#教程
- 2022-03-18 C語言字符串函數操作(strlen,strcpy,strcat,strcmp)詳解_C 語言
- 2022-12-23 Kotlin標準函數與靜態方法基礎知識詳解_Android
- 2022-09-04 使用Docker容器部署MongoDB并支持遠程訪問及遇到的坑_docker
- 2023-02-18 SQLserver中的any和all運算符的用法_MsSql
- 2022-06-23 巧妙使用python?opencv庫玩轉視頻幀率_python
- 2022-08-28 Go通道channel通過通信共享內存_Golang
- 2022-03-04 在項目中 npm i報錯 npm WARN optional SKIPPING OPTIONAL D
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支