2016-07-23 77 views
1

我對C函數指針的編程有着愛/恨的關係。我主要與閃存空間有限的微控制器一起工作,所以顯然我對使用優化感興趣。一方面,功能指針讓我的生活變得如此簡單,因爲我可以編寫一個硬件便攜式軟件模塊,用戶可以在其中提供用於執行硬件相關事情的功能回調(例如:用戶回調以將字節寫入串行端口)。通過這種方式,我可以編寫95%獨立於平臺的代碼,然後快速轉向新硬件。C編程。爲什麼函數指針會破壞優化

在另一方面,我注意到,很多編譯器的優化拋出窗外時,他們看到的任何使用函數指針。例如,我編寫了一個相當通用的函數,它接受一個枚舉類型作爲參數,然後爲每個枚舉類型選擇配置一個巨大的switch/case語句來配置寄存器等,具體取決於用戶傳遞給函數的枚舉類型。用一個不變的文字調用這個函數一次,IE永遠不會改變。構建好的二進制代碼似乎包含了操作代碼來處理每個開關/案例選擇,即使除了其中一個案例選擇都應該被視爲「死代碼」。我使用了不同的優化設置,但爲了得到最小的二進制文件,我必須完全註釋掉每個「case」塊,除了我需要的塊外,這會減少大約1k字節的Flash利用率。如果我不使用函數指針,那麼優化此代碼沒有問題。

奇怪的是,我從來沒有用在我與功能的原型代碼的函數指針。對於我用過的函數指針原型,指針在啓動時建立了一次靜態函數,然後再也不會再次(另一個常量字面賦值)。我知道我大概可以用#defines來完成相同的設計目標,但是讓我感到厭煩的是,這些工具無法根據我的代碼來解釋什麼是可能的/不可能的。

我能看到你有一個函數指針的情況下,和一些動態的東西,你不能預測執行功能(即一個人的類型爲終端,你不先進知道什麼爭論會是)。

有沒有什麼好的理由編譯器掙扎時,函數指針的使用,甚至在可預見的方式來優化?

+4

編譯器停留在DWIS(做我所說)和DWIM(做我的意思)之間。你的函數的代碼說這個函數可以用許多可能的參數值調用,並且應該根據它的調用方式做不同的事情。您的實際使用情況要簡單得多。編譯器的生命是不可能的,除非它完成了「整個程序優化」,因爲它不能告訴你何時可以改變函數的調用方式。函數指針的靈活性會帶來成本。不具有功能指針的靈活性帶來不同的成本。 '太好玩了! –

+2

我認爲這個問題需要示例代碼片斷,它們都帶有或者沒有函數指針,這些函數指針演示了優化問題。 – hyde

回答

2

沒有看到你的代碼,我不能得出任何明確的結論,但這是我從我自己的經驗中知道的。

即使功能是靜態的,只叫一次,指針正在運行時initalised。編譯器不能優化掉直到運行時才知道地址的函數指針。

當函數存在於其他翻譯單元中時,它也不能優化功能指針;它不知道函數在翻譯單元之外的位置,只是它們已經被聲明;鏈接器的工作就是處理它。我相信在理論上LTO可以處理這個問題,但我不確定是否有任何實現。

然而,一些編譯器可優化函數指針(MSVC想到的),如果它知道,所有的地址,如果函數指針在編譯時initalised。

0

如果編譯器可以證明地址是靜態的,那麼這沒有問題。

但一般情況下,這意味着你需要運行的程序,知道哪些功能是實際上調用,所以:

  • 編譯器可能不能靜態地確定函數,因爲它WASN編譯在使用它的同一個TU(翻譯單元)中。
  • 編譯器不能內聯函數
  • 處理器不能優化在所有
1

的基本問題的跳躍是單獨的編譯 - 當你有一個語言(如C),其中不同源文件是單獨編譯的,後來通過單獨的鏈接器鏈接在一起,編譯器優化器在運行時不知道其他編譯單元中的任何內容(這些編譯單元甚至可能還沒有寫入!),所以它沒有知道指針沒有被修改和/或函數沒有被任何其他參數調用的方式。

一個可能的解決方案是鏈接時優化 - 如果您的開發套件支持它。

另一種方法是將所有內容強制編譯爲一個編譯單元,並將所有內容都編譯爲static,以便編譯器知道其他編譯單元不能引用它。這可能會讓優化器做得更好。

相關問題