C語言函數指針

一、函數指針的定義

顧名思義,函數指針就是函數的指針。它是一個指針,指向一個函數。看例子:
A)
char * (*fun1)(char * p1,char * p2);
B)
char * *fun2(char * p1,char * p2);
C)
char * fun3(char * p1,char * p2);

看看上面三個表達式分別是什么意思?
C):這很容易,fun3 是函數名,p1,p2 是參數,其類型為char *型,函數的返回值為char *類型。
B):也很簡單,與C)表達式相比,唯一不同的就是函數的返回值類型為char**,是個二級指針。
A):fun1 是函數名嗎?回憶一下前面講解數組指針時的情形。我們說數組指針這么定義或許更清晰:
? ?int (*)[10] p;
再看看A)表達式與這里何其相似!明白了吧。這里fun1 不是什么函數名,而是一個指針變量,它指向一個函數。這個函數有兩個指針類型的參數,函數的返回值也是一個指針。同樣,我們把這個表達式改寫一下:char * (*)(char * p1,char * p2) fun1; 這樣子是不是好看一些呢?只可惜編譯器不這么想。^_^。

二、函數指針的使用

1、函數指針使用的例子
上面我們定義了一個函數指針,但如何來使用它呢?先看如下例子:
#include <stdio.h>
#include <string.h>
char * fun(char * p1,char * p2)
{
? ?int i = 0;
? ?i = strcmp(p1,p2);
? ?if (0 == i)
? ?{
? ? ? return p1;
? ?}
? ?else
? ?{
? ? ? return p2;
? ?}
}
intmain()
{
? ?char * (*pf)(char * p1,char * p2);
? ?pf = &fun;
? ?(*pf) ("aa","bb");
? ?return 0;
}
我們使用指針的時候,需要通過鑰匙(“*”)來取其指向的內存里面的值,函數指針使用也如此。通過用(*pf)取出存在這個地址上的函數,然后調用它。這里需要注意到是,在Visual C++6.0 里,給函數指針賦值時,可以用&fun 或直接用函數名fun。這是因為函數名被編譯之后其實就是一個地址,所以這里兩種用法沒有本質的差別。這個例子很簡單,就不再詳細討論了。

2、*(int*)&p ----這是什么?
也許上面的例子過于簡單,我們看看下面的例子:
void Function()
{
? ?printf("Call Function!\n");
}
intmain()
{
? ?void (*p)();
? ?*(int*)&p=(int)Function;
? ?(*p) ();
? ?return 0;
}
這是在干什么?*(int*)&p=(int)Function;表示什么意思?別急,先看這行代碼:
? ?void (*p)();
這行代碼定義了一個指針變量p,p 指向一個函數,這個函數的參數和返回值都是void。

&p 是求指針變量p 本身的地址,這是一個32 位的二進制常數(32 位系統)。
(int*)&p 表示將地址強制轉換成指向int 類型數據的指針。
(int)Function 表示將函數的入口地址強制轉換成int 類型的數據。

分析到這里,相信你已經明白*(int*)&p=(int)Function;表示將函數的入口地址賦值給指針變量p。那么(*p) ();就是表示對函數的調用。

講解到這里,相信你已經明白了。其實函數指針與普通指針沒什么差別,只是指向的內容不同而已。

使用函數指針的好處在于,可以將實現同一功能的多個模塊統一起來標識,這樣一來更容易后期的維護,系統結構更加清晰。或者歸納為:便于分層設計、利于系統抽象、降低耦合度以及使接口與實現分開。

3、(*(void(*) ())0)()------這是什么?
是不是感覺上面的例子太簡單,不夠刺激?好,那就來點刺激的,看下面這個例子:
? ?(*(void(*) ())0)();
這是《C Traps and Pitfalls》這本經典的書中的一個例子。沒有發狂吧?下面我們就來分析分析:
第一步:void(*) (),可以明白這是一個函數指針類型。這個函數沒有參數,沒有返回值。
第二步:(void(*) ())0,這是將0 強制轉換為函數指針類型,0 是一個地址,也就是說一個函數存在首地址為0 的一段區域內。
第三步:(*(void(*) ())0),這是取0 地址開始的一段內存里面的內容,其內容就是保存在首地址為0 的一段區域內的函數。
第四步:(*(void(*) ())0)(),這是函數調用。

好像還是很簡單是吧,上面的例子再改寫改寫:
? ?(*(char**(*) (char **,char **))0) ( char **,char **);
如果沒有上面的分析,肯怕不容易把這個表達式看明白吧。不過現在應該是很簡單的一件事了。讀者以為呢?

4、函數指針數組
現在我們清楚表達式“char * (*pf)(char * p)”定義的是一個函數指針pf。既然pf 是一個指針,那就可以儲存在一個數組里。把上式修改一下:
? ?char * (*pf[3])(char * p);
這是定義一個函數指針數組。它是一個數組,數組名為pf,數組內存儲了3 個指向函數的指針。這些指針指向一些返回值類型為指向字符的指針、參數為一個指向字符的指針的函數。這念起來似乎有點拗口。不過不要緊,關鍵是你明白這是一個指針數組,是數組。

函數指針數組怎么使用呢?這里也給出一個非常簡單的例子,只要真正掌握了使用方法,再復雜的問題都可以應對。如下:
#include <stdio.h>
#include <string.h>
char * fun1(char * p)
{
? ?printf("%s\n",p);
? ?return p;
}
char * fun2(char * p)
{
? ?printf("%s\n",p);
? ?return p;
}
char * fun3(char * p)
{
? ?printf("%s\n",p);
? ?return p;
}
intmain()
{
? ?char * (*pf[3])(char * p);
? ?pf[0] = fun1; // 可以直接用函數名
? ?pf[1] = &fun2; // 可以用函數名加上取地址符
? ?pf[2] = &fun3;
? ?pf[0]("fun1");
? ?pf[0]("fun2");
? ?pf[0]("fun3");
? ?return 0;
}

5、函數指針數組的指針
看著這個標題沒發狂吧?函數指針就夠一般初學者折騰了,函數指針數組就更加麻煩,現在的函數指針數組指針就更難理解了。

其實,沒這么復雜。前面詳細討論過數組指針的問題,這里的函數指針數組指針不就是一個指針嘛。只不過這個指針指向一個數組,這個數組里面存的都是指向函數的指針。僅此而已。

下面就定義一個簡單的函數指針數組指針:
? ?char * (*(*pf)[3])(char * p);
注意,這里的pf 和上一節的pf 就完全是兩碼事了。上一節的pf 并非指針,而是一個數組名;這里的pf 確實是實實在在的指針。這個指針指向一個包含了3 個元素的數組;這個數字里面存的是指向函數的指針;這些指針指向一些返回值類型為指向字符的指針、參數為一個指向字符的指針的函數。這比上一節的函數指針數組更拗口。其實你不用管這么多,明白這是一個指針就ok 了。其用法與前面講的數組指針沒有差別。下面列一個簡單的例子:
#include <stdio.h>
#include <string.h>
char * fun1(char * p)
{
? ?printf("%s\n",p);
? ?return p;
}
char * fun2(char * p)
{
? ?printf("%s\n",p);
? ?return p;
}
char * fun3(char * p)
{
? ?printf("%s\n",p);
? ?return p;
}
intmain()
{
? ?char * (*a[3])(char * p);
? ?char * (*(*pf)[3])(char * p);
? ?pf = &a;
? ?a[0] = fun1;
? ?a[1] = &fun2;
? ?a[2] = &fun3;
? ?pf[0][0]("fun1");
? ?pf[0][1]("fun2");
? ?pf[0][2]("fun3");
? ?return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/450548.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/450548.shtml
英文地址,請注明出處:http://en.pswp.cn/news/450548.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

程序員進階之算法練習:LeetCode專場

歡迎大家前往騰訊云社區&#xff0c;獲取更多騰訊海量技術實踐干貨哦~ 本文由落影發表 前言 LeetCode上的題目是大公司面試常見的算法題&#xff0c;今天的目標是拿下5道算法題&#xff1a; 題目1是基于鏈表的大數加法&#xff0c;既考察基本數據結構的了解&#xff0c;又考察在…

vim 安裝vim-prettier

1、在.vimrc中添加 配置沒有安裝成功的話 git clone https://github.com/prettier/vim-prettier Plug prettier/vim-prettier, { do: yarn install, for: [javascript, typescript, css, less, scss, json, graphql, markdown, vue, yaml, html, php] } let g:prettier#aut…

詳解Mysql中的JSON系列操作函數

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 一、方法羅列&#xff1a; 分類 函數 描述創建jsonjson_array 創建json數組json_object 創建json對象 json_quote 將json轉成json字符串…

WEB/H5性能優化總結

我們今天來說說前端圖形渲染優化&#xff0c;因為我接下來的時間可能要開始研究webgl方面的東西&#xff0c;所以就在這里把之前做過的H5做一個總結&#xff0c;現同步發布于GERRY_BLOG&#xff0c;TiMiGerry-知乎&#xff0c;轉載請保留鏈接。靜態資源-圖片 一 、圖片格式JPEG…

C語言數組參數與指針參數

我們都知道參數分為形參和實參。形參是指聲明或定義函數時的參數&#xff0c;而實參是在調用函數時主調函數傳遞過來的實際值。 一、一維數組參數 1、能否向函數傳遞一個數組&#xff1f;看例子&#xff1a;void fun(char a[10]){char c a[3];}intmain(){char b[10] “abcd…

maven文件結構

pom.xml 用于maven的配置文件 /src 源代碼目錄 /src/main 工程源代碼目錄 /src/main/java 工程java源代碼目錄 /src/main/resource 工程的資源目錄 /src/test 單元測試目錄 /src/test/java /target 輸出目錄&#xff0c;所有的輸出都存放在這個目錄下 /target/classes 編譯之…

php如何使用高階函數

1、首先學會數組轉集合的方式 &#xff08;1&#xff09;使用collect函數 $arr [1, 2, 3, 4, 5]; $collect collect($arr); &#xff08;2&#xff09;使用array_map函數 $arr [1, 2, 3, 4, 5]; $collect array_map(function($item){ return $item *…

Git 使用,命令說明

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. D:\ChengXu\git\Git中雙擊Git Bash啟動git窗口。 2. 這條不能放到博客&#xff0c;是我的賬號密碼。 3. 添加&#xff1a; git add …

2017ACM/ICPC亞洲區沈陽站 C Hdu-6219 Empty Convex Polygons 計算幾何 最大空凸包

題面 題意:給你一堆點,求一個最大面積的空凸包,里面沒有點. 題解:紅書板子,照抄完事,因為題目給的都是整點,所以最后答案一定是.5或者.0結尾,不用對答案多做處理 1 #include<bits/stdc.h>2 #define N 553 using namespace std;4 struct rec5 {6 double x,y;7 };8 rec…

python讀xml文件

# -*- coding:utf-8 -*- import jsonimport requestsimport oscurpathos.path.dirname(os.path.realpath(__file__))xmlpathos.path.join(curpath,read1.xml)with open(xmlpath,encoding"utf-8") as fp: bodyfp.read() print(body)轉載于:https://www.cnblogs.…

C語言數組應用

一、數組的內存布局 先看下面的例子&#xff1a;int a[5];所有人都明白這里定義了一個數組&#xff0c;其包含了5 個int 型的數據。我們可以用a[0],a[1]等來訪問數組里面的每一個元素&#xff0c;那么這些元素的名字就是a[0],a[1]…嗎&#xff1f;看下面的示意圖&#xff1a; 如…

Installation failed, deleting ./composer.json.安裝phpunit報錯解決方案

是因為你沒有裝全局的phpunit&#xff0c;安裝命令 composer global require phpunit/phpunit 之后你輸入 composer require --dev phpunit/phpunit 就發現你安裝成功了

MyBatis在Oracle中插入數據并返回主鍵的問題解決

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 前言&#xff1a;我早期用過這個方法&#xff0c;但是返回的依舊是影響行數&#xff0c;不是主鍵。 只是這種寫法可以達到我要的效果&a…

在 Intellij IDEA 里使用 OpenJFX (JavaFX)

2019獨角獸企業重金招聘Python工程師標準>>> JDK 11 把 JavaFX 剝離了出來&#xff0c;形成了單獨且開源的 OpenJFX 模塊。 本文的目的是通過簡單的例子解釋這一變化對使用 JavaFX 所造成的影響&#xff0c;并找到一種在 IDEA 2018.2 上使用它的辦法。 首先&#xf…

使用phpunit新建項目

1、mkdir test-project 新建一個test-project 2、cd test-project 跑到文件夾中 3、實例化git git init 4、新建phpunit項目 composer require --dev phpunit/phpunit 5、使用gi實例化.gitignore gi composer>.gitignore (如果沒有安裝gi&#xff0c;請使用命令ec…

如何解決eclipse里面tomcat 8080端口被占用

很多時候運行tomcat 的時候總是會提示tomcat 的端口被占用 但是任務管理器里面還找不到是哪個端口被占用了 因此很多人就重新配置tomcat 或者去修改tomcat的端口號 &#xff0c;其實這么做太麻煩了 &#xff0c;小弟在這里告訴你一個非常簡單的方法。 1.在開始菜單中選擇運行 …

Selenium UI 舉例 getCssValue

selenium jar包中&#xff0c;在WebElement的接口中&#xff0c; String getCssValue(String var1);可以通過標簽&#xff0c;獲取對應的css值。具體要怎么用呢&#xff0c;如下&#xff1a; WebElement baidu driver.findElement(By.id("su"));su.getCssValue(&quo…

java集合框架中contains(),containsKey()和containsValue()的用法

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 java集合框架中contains(),containsKey()和containsValue()的用法&#xff1a; List集合的contains()方法用于判斷集合中包不包含某個元…

敏捷視頻

規模化極限編程的關鍵抓手&#xff1a;驗收條件https://mp.weixin.qq.com/s/aHlSxpMx7DTQXaoEgcAQ3g 5分鐘讓你子解持續集成https://www.bilibili.com/video/BV1SK411W77W/?spm_id_fromtrigger_reload 5分鐘讓你學會返工率降低1倍的神技--開卡、驗卡https://www.bilibili.com/…

提問的智慧

提問的智慧轉載于:https://www.cnblogs.com/whigym/p/10028642.html