C語言結構體內部的函數指針有什麼意義?
突然想到了這個問題,寫與不那樣寫有什麼區別?
目測是這麼寫的人想用來模擬C++的類成員函數,這沒毛病
但有一點,如果這麼做模擬的不是虛函數的話,會比C++多性能損失
當然是用來放函數指針啊,不然呢?
註冊一批不同用途的回調函數可以用結構體啊,如果保存回調函數需要保存附加數據的話,也可以將它和回調函數一起放在結構體里。
例如:
除了模擬虛表還有可能是回調。很多 C++ 的 GUI 框架也這麼搞。
C語言就是實現以下這些東西,指針,函數指針就是下面引用相關的那一段。
變數
一般想學一個語言,先學變數,變數一般分為兩種,值類型和引用類型。Java,C#,Objective-C,python。都有這些東西。入手一個語言先熟悉,這門語言的兩種變數,值變數,引用變數。
值類型
值類型是地址裡面直接存一個值。這些類型傳遞的是值和傳遞地址的類型有很多不同的特性。值類型分配在棧上。裝箱的值類型會分配在堆上。值類型包括:數值類型,結構體,bool型,用戶定義的結構體,枚舉,可空類型。值類型的變數直接存儲數據,分配在託管棧中。變數會在創建它們的方法返回時自動釋放。
byte
short int long long longchar
double
floatboolean
。。。。。。。引用類型
一般想學一個語言,一般都會有一類引用類型。引用類型包括:數組,用戶定義的類、介面、委託,object,字元串,null類型,類。引用類型的變數持有的是數據的引用,數據存儲在數據堆,分配在託管堆中,變數並不會在創建它們的方法結束時釋放內存,它們所佔用的內存會被垃圾回收機制釋放。
引用類型-結構類型
結構類型,主要是用來組織變數。用來實現常見的數據結構。棧,隊列,二叉樹,圖。。。。等等。
字元串 集合 列表 字典可變類型
時常我們會遇到一個概念是可變的類型和不可變的類型,這個可變與不可變是對地址來說的。對於不可變的字元串不管多少個拷貝,我們都將引用指向同一個地方就可以,這個問題實際就是編程中寫出來的一些bug的源頭。拷貝
深拷貝,淺拷貝,對於可變和不可變類型的意義是不一樣的。實際本質是我們是用一個地址指向了同一個地址,還是從新分配了一片內存,重新將值賦給新分配的內存。引用類型-過程類型
過程類型主要用來調用或者封裝調用,將對變數的各種操作封裝成函數,函數多了不便於組織將很多函數可以封裝成類,類可以單繼承,多繼承,有類方法,實例方法。需要組織多個類就出現了包,一個包不想讓別人知道實現細節變出現了庫。函數時常被我們理解成一個調用過程,實際它也存在一個地址只,非同步回調的是函數,調用的是函數的地址,函數的指針。介面,委託,函數,類中的函數都可以理解為函數,函數指針。介面和代理是實現函數指針的抽象,類是將函數指針和變數做為一個整體。寫程序時常是調用系統的包,類,函數,其實都是這樣一個過程,我們要在會用的同時知道原理。
函數 學各種語言寫hello world都會調用函數。 類 介面 委託 包值類型和引用類型的區別
這個總結很常見也很實用,面試百分之90中獎。
值類型
存儲方式:存儲值
內存分配:分配在棧上內存回收:用完回收
效率:效率高 轉換:裝箱才能變成引用類型引用類型
存儲方式:存儲地址
內存分配:分配在堆上 內存回收:手動回收和垃圾回收 效率:效率低 轉換:引用類型需要拆箱才能變成值類型內存-作用域-生命周期-垃圾回收
內存
這篇講的是變數,所有變數怎麼跑也跑不出內存。一旦有不理解的問題請向內存追尋。這就是大學學操作系統的原因。內存是怎麼分配的就是問題的源泉。
作用域
最近寫代碼犯過一個教科書式的錯誤,#define的變數為什麼不生效,#define變數的作用域只是當前文件。注意變數的作用域。
垃圾回收
強引用,弱引用,引用環。垃圾回收時機,垃圾回收的方式。也是學習一個語言時要想明白的問題。
模擬OOP中的虛函數。
函數指針幾乎總是為了運行時多態。
但是,這樣跟寫在外面的函數 操作結構體變數有什麼區別嗎?
為了讓「使用哪個函數」這件事情,和某個具體的數據實例在一起。
一種虛函數特性的模擬。和 C++ 通常實現的區別是去掉了 vptr 這層間接,把虛表嵌到每個對象里。
偶爾看到這個問題,看了上面的回答不是很全面,補充一下,C語言的結構體內部函數指針和C++的虛函數類似,都是為了提供向上抽象,但意義不同,C++的虛函數是為了實現面向對象編程,而C語言結構體內部函數指針是為了實現模塊化編程,比如要註冊一個新的模塊,只需提供包含該模塊必需的參數以及初始化函數指針,或過程調用指針,而不必關心該模塊內部的變數和函數的具體聲明,這樣就實現了模塊的封裝,使得一個大的應用程序可以被分割成多個小的模塊獨立進行開發,具體可以參考GitHub上的一些開源項目,比如nginx就是高度模塊化的,裡面很多結構體都封裝了相應的初始化或過程調用函數指針,這也就是為什麼可以說一個程序的好壞,可以從其數據結構的定義一窺端倪。
在低級語言里模擬高級語言的特性,在低級產品中榨出高級產品的功能是極客們樂此不疲的娛樂。
此例應不是模仿,是C語言應用面向對象思想的實踐。面向對象思想出現的歷史很早的.看了一圈答案,感覺很多都在說模擬這模擬那啥的。看來都不是真正的C程序員!
C就是C,不是C++的子集,而是C++他爸。好了,言歸正傳。
首先說一句:函數指針就是指針,不同的是它指向函數。這個就是C語言的一個語法,然後再說它的應用。函數指針的應用場景很廣,大體可以總結如下:
1,一般的函數指針,指向函數,實現比如勾子函數等。2,回調函數,實現在特定情況下需要不同的處理,或者對不同些數據的不同處理。3,封裝。也就是題主問的struct中的函數指針的用途。
下面對第三點展開一下,有的放矢的回答題主的問題。結構體,是定義了一個用戶自定義內型,那麼這個內型就可以定義N多的變數,不同的變數擁有不同的值,也包括函數指針的值,這樣就實現了"多態",或者說是統一的入口。這句話是重點。再重複一遍,不同的實現對應著具體的實現,而調用方卻是看到的統一的入口。如此就實現了"多態"。再說兩句,面向對象的思想是在C的大量開發中總結出來的,C雖然不是一門面向對象語言,但是它包羅萬象。上面的例子很多,設備驅動,文件等大量使用。此外,在模塊化編程中,這個是利器,是重器,是法寶!模擬面向對象。沒有類,沒有成員函數,拿函數指針湊合下。
假裝在面向對象,把函數指針當作struct的一個成員,也可以通過數組實現類似鍵值對的綁定
如下為jos 中kernel部分代碼
可以實現命令名字與命令調用函數的綁定模擬面向對象唄,struct裡面加一個函數指針可以模擬對象的方法,加一個結構體指針可以模擬繼承,可以重寫父類函數...
我見過有人C++的結構體也寫函數指針的(?ω?)
有下面幾種可能
1,作為結構體實例的方法,類似對象方法2,回調3,就是簡單函數指針指針在c系列語言中的語義是弱引用語義,因此可以靈活用來處理很多東西。對於面向對象的模擬在結構體中使用非常廣泛,比如linux驅動模型框架,基本都是這種機制。回調,使用結構體保存上下文,也是常用的方法。就是整理一下函數指針,c又沒namespace啊。。。往往用來隱喻:這個函數就是操作本結構體的。你類比c++的方法。
我挺喜歡這樣的風格的。
用來實現虛函數的
比較常用的一種方式,用於設置callback,用一個函數指針,可以匹配到多個聲明形式相同的函數上。
利用結構體假裝自己在OOC,如果只是為了這個的話那就並沒有卵用。
不過說不定人家有別的用途呢……沒看到代碼啥都不好說。這樣結構體裡面不僅可以存放數據了,還可以將數據與操作捆綁在一起,我不是一定說在模擬面對對象,這樣寫可以方便操作提高擴展性,可以在一定程度上增加程序的抽象層次
不編了不編了…扯著蛋了。實話就是想裝逼,沒別的了,再問感覺沒有像別的高級語言那樣有類作用域,所以這個東西寫出來是不是徒有其型?
其實上個世紀的常見做法是用來做鏈表存下個結構體位置吧?
推薦閱讀:
※一條C語言語句不一定是原子操作,但是一個彙編指令是原子操作嗎?
※c語言函數是如何獲取傳入的數組(指針)的指針所指向內容的長度的,有辦法嗎?
※C語言零基礎想要自學,有什麼書可以推薦一下嗎?
※寫操作系統只能用彙編和 C 語言嗎?
※不同的IDE在編譯代碼時是否存在區別?如果有,那請問區別是什麼?
TAG:C編程語言 |