2009-01-27 70 views
3

我們嘗試嵌入二進制對象中的什麼字符串,以便我們可以看到已部署的可執行文件或共享庫的版本號優化掉。通常我們在這個字符串中嵌入標準的CVS ID信息。例如,我們可能嵌入:SCCS「是什麼」的字符串不是由編譯器

const char cvsid[] = "@(#)OUR_TEAM_staging_remap_$Revision: 1.30 $ $Name: $"; 

內的C代碼。

從人(1)內容:

的什麼工具搜索 模式@(#)的SCCS get命令的出現在每個文件名(見SCCS-得到(1)) 替代品在@(#)ID關鍵字,並打印下文 直至」,>,NEWLINE,\或NULL字符。

有這個變量的僅一個實例,它是從未提及。有人暗示這可能會被編譯器優化掉

我一直在使用這種技術多年在C和C++,並與各種編譯器,我還沒有看到一個什麼樣的字符串優化掉。

任何人都知道他們爲什麼沒有被優化掉?

回答

2

直到最近(我發現在2005年中期的問題),這是可以使用:

static const char sccs[] = "@(#)%W% %E%"; 

或源代碼和海灣合作委員會和其他大多數編譯器類似的東西不會優化它拿走。從大約那個時候發佈GCC(可能是GCC 4.0.x,源自2005年4月)開始,那些常量字符串被排除在二進制文件之外。所以,我不得不修改我的源代碼以使變量在外部可見。編譯器無法單獨查看目標文件,並斷定該字符串未被使用,因爲文件外部的某些內容可能會引用它。所以,我的文件現在包含:

#ifndef lint 
extern const char jlss_id_filename_c[]; 
const char jlss_id_filename_c[] = "@(#)$Id$"; 
#endif /* lint */ 

好的 - 這是一個混合;我真的使用RCS來存儲源代碼,但我仍然更喜歡whatident來識別文件 - 另外我有我自己的黑客what,這兩個whatident加上我自己的一些調整。但是我有一些文件中的聲明 - 不是全部 - 以及所有文件中的定義。 (在一些警告標誌的設置下,現在還沒有記住,當變量在聲明之前被定義時,我收到了警告,這可能是GCC的一個變化,可以解決這個問題;我不再確定)

當我創建一個新文件時,我的模板生成器用正在生成的文件的適當名稱替換'filename_c'。對於標題也是如此 - 儘管標識字符串僅嵌入到一個文件中以避免多個定義。

我更喜歡使用靜態常量的舊系統 - 但這對我來說已經工作了3年多。

+0

如果我們不走運,你可以再用這個技巧3年,直到谷歌gcc人們用他們的[WHOPR努力[1]取得進展,這會使你的技巧過時;-) [1]: http://gcc.gnu.org/projects/lto/whopr.pdf – none 2009-05-18 03:42:29

2

通常這種情況不會發生,因爲多餘的字符串有小的成本,並且可以在這樣的情況下是有用的,等等(例如存儲一串字符串資源的,只有先在代碼實際引用)。

+0

+1編譯器可能不知道您沒有以其他方式引用它。此外,將其添加到數據部分也沒有什麼壞處。 – user7116 2009-01-27 17:38:43

2

它們可能沒有被優化掉,因爲你的編譯器知道這些字符串可以用於這些目的。

當然,編譯器是完全允許優化它拿走,只要程序的行爲,更準確的觀察到的行爲,也不會改變。這意味着對揮發物的寫入和讀取順序,以及對庫函數的調用都不會改變。

如果您的應用程優化這樣的字符串,我想這種行爲不會改變。但編譯器希望可以使用,並且不會以用戶的方式觸發。這就是爲什麼他們也包含有用的擴展。如果你想確保它偶爾沒有被優化,可以看看編譯器擴展。 GCC具有unused屬性,這使得它不會針對未使用的對象發出警告。也許這樣或類似的東西可以幫助你的變量沒有被優化掉。

從語言的立場來看,有ISN是一個工具,雖然強制編譯器保留它。

編輯:有一個有關該主題here usenet的帖子,有用的答案。

1

微軟的Visual C++ 2005有一個鏈接器選項,它應該控制它對未使用數據的處理:/OPT:UNREF強制鏈接器保留未使用的數據,/OPT:REF允許它將其消除。

然而,在我的簡單測試,選擇對的聲明

static char VersionString[] = "HELLO_WORLD 2.0"; 

的字符串出現在了發佈和調試二進制文件都不管的標誌沒有影響。

+0

這可能取決於您使用的優化級別。 – 2009-01-31 07:26:41

1

如果沒有「static」關鍵字,則變量不能被優化,因爲另一個模塊可能會聲明一個引用(使用extern)。由於C/C++通常每次編譯一個文件,因此編譯器無法知道是否存在外部引用。

通過添加static關鍵字,您可以告訴編譯器該名稱僅在編譯過程中可見,並且使用並優化它不成爲可能。

我認爲鏈接器可以檢測一個未使用的全局變量,如果對象格式允許,也可以優化它,儘管我不確定任何人。

1

(是的,我知道這是問及年齡前回答,但回答的這個新款式是可用的,所以....)

gcc(至少在3.3和之後),現在有編譯器指令__attribute__((unused))將一個變量標記爲「知道它可能不會引用「來取消警告,並且__attribute__((used))將它標記爲已使用(因此不是被優化的候選),即使沒有其他代碼實際引用它。

因此,這可能會爲你做到這一點:

static const char what_ident[] __attribute__((used)) = "@(#) $Id$"; 

如果linker還優化了它,那麼你可能需要把它放在被標記爲段保持無論鏈接時引用。

static const char what_ident[] __attribute__((section("what"), used)) = "@(#) $Id$"; 

,並添加gcc的選項-Wl,-bkeepfile:file.o所以在文件中的鏈接器不會抑制未引用部分。c的輸出。