簡體   English   中英

外部變量只在標題中意外工作,為什么?

[英]Extern variable only in header unexpectedly working, why?

我目前正在為Arduino更新一個C ++庫(特別是使用avr-gcc編譯的8位AVR處理器)。

通常,默認Arduino庫的作者喜歡在頭文件中包含類的extern變量,該變量也在類.cpp文件中定義。 我假設這基本上是為了讓新手作為內置對象准備好了。

我的方案是:我更新的庫不再需要.cpp文件,我已將其從庫中刪除。 直到我進行最后一次檢查才發現我發現的錯誤,盡管事實上沒有為.cpp文件中的extern變量提供定義,但沒有產生鏈接器錯誤。

這很簡單,我可以得到它(頭文件):

struct Foo{
  void method() {}
};

extern Foo foo;

包含此代碼並在一個或多個源文件中使用它不會導致任何鏈接器錯誤。 我在兩個版本的GCC中嘗試過它,Arduino使用(4.3.7,4.8.1)和啟用/禁用C ++ 11。

在我試圖導致錯誤的過程中,我發現只有在執行諸如獲取對象的地址或修改我添加的虛擬變量的內容之類的操作時才有可能。

在發現這一點后,我發現其重要的是要注意:

  • 類函數只返回其他對象,就像在運算器中返回對自身的引用,甚至是副本一樣。
  • 它只修改外部對象(代碼中有效的volatile uint8_t引用的寄存器),並返回其他類的臨時對象。
  • 此標頭中的所有類函數都是如此基本,以至於它們的成本低於或等於函數調用的成本,因此它們(在我的測試中)完全內聯到調用者中。 典型的語句可能會在調用鏈中創建許多臨時對象,但是編譯器會通過這些查找並直接輸出有效的代碼修改寄存器,而不是一組嵌套的函數調用。

我還記得在n3797 7.1.1 - 8中讀取extern可用於不完整類型,但是類是完全定義的,而聲明不是(這可能是不相關的)。

我被引導相信這可能是游戲優化的結果。 我已經看到了獲取地址對對象的影響,否則這些對象將被視為常量並且在沒有使用RAM的情況下進行編譯。 通過向編譯器無法保證狀態的對象添加任何間接層,將導致此RAM消耗行為。

所以,也許我只是通過詢問就回答了我的問題,但是我仍然在做假設並且困擾我。 經過相當長一段時間的愛好編碼C ++,字面意思是我的do-not列表中唯一的東西就是做出假設

真的,我想知道的是:

  • 關於我的工作解決方案,是否記錄了無法獲取類的地址(導致間接)的簡單情況?
  • 它只是由優化引起的邊緣情況行為,消除了對某些內容的需求嗎?
  • 或者是簡單明了的未定義行為。 因為在GCC中可能有一個錯誤並允許在優化降低或禁用時可能失敗的代碼?

或者你們中的一個人可能很幸運擁有一個解碼器環,可以在標准中找到合適的段落,概述具體細節。

這是我的第一個問題,所以如果您想了解某些細節,請告訴我。如果需要,我還可以提供GitHub鏈接。

編輯:由於庫需要與現有代碼兼容,我需要保持使用點語法的能力,否則我只需要一類靜態函數。

為了刪除現在的假設,我看到兩個選項:

  • 僅為變量聲明添加.cpp。
  • 在標題中使用#define foo (Foo())的define,允許通過臨時的點語法。

我更喜歡使用定義的方法,社區的想法是什么?

干杯。

聲明一些extern只是通知匯編器和鏈接器,無論何時使用該標簽/符號,它都應該引用符號表中的條目,而不是本地分配的符號。

鏈接器的作用是盡可能用符號表條目替換地址空間的實際引用。

如果您在C文件中根本不使用該符號,它將不會顯示在匯編代碼中,因此當您的模塊與其他模塊鏈接時不會導致任何鏈接器錯誤,因為沒有未定義的引用。

它是由優化引起的邊緣情況行為,或者您從不在代碼中使用foo變量。 我不是100%肯定它在形式上不是一個未定義的行為,但我很確定它從實際的角度來看並不是不確定的。

extern變量以這種方式實現,用它們編譯的代碼產生所謂的重定位 - 應該放置變量addres的空位 - 然后由鏈接器填充。 顯然, foo從未在您的代碼中以需要獲取其地址的方式使用,因此鏈接器甚至不會嘗試找到該符號。 如果關閉優化(-O0),可能會出現鏈接器錯誤。

更新:如果你想保留“點符號”但刪除未定義extern的問題,你可以用static (在頭文件中)替換extern ,為每個TU創建單獨的變量“實例”。 由於這個變量無論如何都會被優化,這根本不會改變真正的代碼,但也可以用於未經優化的構建。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM