2009-08-20 65 views
5

所以我通過展開一些循環來優化一些代碼(是的,我知道我應該依靠我的編譯器爲我做這件事,但我沒有選擇編譯器)和我想稍微優雅一些​​,以防萬一我的數據大小因未來的一些編輯而發生變化,代碼將會優雅地降級。宏的等效C sizeof

喜歡的東西:

typedef struct { 
    uint32_t alpha; 
    uint32_t two; 
    uint32_t iii; 
} Entry; 

/*...*/ 

uint8_t * bytes = (uint8_t *) entry; 
#define PROCESS_ENTRY(i) bytes[i] ^= 1; /*...etc, etc, */ 
#if (sizeof(Entry) == 12) 
    PROCESS_ENTRY(0);PROCESS_ENTRY(1);PROCESS_ENTRY(2); 
    PROCESS_ENTRY(3);PROCESS_ENTRY(4);PROCESS_ENTRY(5); 
    PROCESS_ENTRY(6);PROCESS_ENTRY(7);PROCESS_ENTRY(8); 
    PROCESS_ENTRY(9);PROCESS_ENTRY(10);PROCESS_ENTRY(11); 
#else 
# warning Using non-optimized code 
    size_t i; 
    for (i = 0; i < sizeof(Entry); i++) 
    { 
     PROCESS_ENTRY(i); 
    } 
#endif 
#undef PROCESS_ENTRY 

這不工作,當然,因爲sizeof是不提供給預處理器(至少,這是this answer似乎什麼來表示)。

有沒有一種簡單的解決方法,我可以用它來獲得用於C宏的數據結構sizeof,還是我只是SOL?

+0

那麼,sizeof()**是一個宏。一個內置的宏,至少。 – Havenard 2009-08-20 21:01:30

+5

sizeof是不是一個宏,在任何形狀或形式 – 2009-08-20 21:08:29

+2

sizeof是不是一個宏,雖然offsetof是。 sizeof更像是一個運營商。 – 2009-08-20 21:09:16

回答

17

你不能在預處理器中執行,但不需要。剛生成宏平原if

#define PROCESS_ENTRY(i) bytes[i] ^= 1; /*...etc, etc, */ 
if (sizeof(Entry) == 12) { 
    PROCESS_ENTRY(0);PROCESS_ENTRY(1);PROCESS_ENTRY(2); 
    PROCESS_ENTRY(3);PROCESS_ENTRY(4);PROCESS_ENTRY(5); 
    PROCESS_ENTRY(6);PROCESS_ENTRY(7);PROCESS_ENTRY(8); 
    PROCESS_ENTRY(9);PROCESS_ENTRY(10);PROCESS_ENTRY(11); 
} else { 
    size_t i; 
    for (i = 0; i < sizeof(Entry); i++) { 
     PROCESS_ENTRY(i); 
    } 
} 

sizeof是一個常量表達式,並比較與常一個常數也不變。任何理智的C編譯器都會優化掉編譯時始終爲false的分支 - 常量摺疊是最基本的優化之一。不過,你失去了#warning

+0

從稍微不同的角度來看待問題總是很好的。 +1和榮譽! – qrdl 2009-08-21 06:16:01

9

如果您使用的是autoconf或其他構建配置系統,則可以在配置時檢查數據結構的大小並寫出標題(如#define SIZEOF_Entry 12)。當然,這在交叉編譯等情況下變得更加複雜,但我假設你的構建和目標體系結構是相同的。

否則是的,你運氣不好。

-1

如果您希望結構體的最小可能大小(或將其與4字節邊界對齊,或其他),則可以使用打包或對齊的屬性。

在Visual C++中,你可以使用#pragma pack,並且在GCC可以使用__attribute __((包裝))和__attribute __((對齊(NUM字節))

+0

這不會給大小。此外,雖然打包將節省空間,但這可能會花費時間,而這正是他想要獲得的。 – 2009-08-20 21:12:15

+0

@David Thornley:它不會給出尺寸,但它會做他想在這個代碼塊中做的事情。通過對結構進行打包,他肯定會知道填充不會妨礙,所以結構*只有12個字節(或每個項目的大小總和),因此不需要使用預處理器宏找到結構的大小。 PROCESS_ENTRY在未填充的結構上使用字節級訪問,因此打包結構可以使用此宏而不必擔心填充。 – 2009-08-20 21:37:06

+0

我應該補充說,通常最好不要干涉,只是讓優化器做它的工作(特別是因爲它比大多數程序員好得多)。 – 2009-08-20 22:56:02

5

你的運氣了 - 預處理器甚至不知道結構是什麼,更不用說用任何方法來計算它的大小了。更不用說用任何方式來計算它的大小了。更不用說用任何方法來計算它的大小了。

在這樣的情況下,你可以僅僅定義一個常量來知道結構的大小,靜態地斷言它實際上等於使用負尺寸陣列技巧的尺寸。

你也可以試試if (sizeof(Entry) == 12),並查看您的編譯器是否能夠在編譯時評估分支條件並刪除死代碼。這不是一個很大的問題。

+0

+1爲'看優化器是否優化'建議。 – 2009-08-20 21:30:33

+0

使用assert()而不是#warning的想法也很好。你真的想要一個優雅的降級,還是你想清楚地知道這個問題,所以你可以修復它? – 2009-08-21 18:24:05

+0

有兩個原因,它不能是12.一個是有一些意想不到的填充(在這種情況下,你可能想要一個錯誤,所以你可以使用編譯器特定的編譯指令打包結構),一個是你已經添加一個字段(在這種情況下,您希望更新#define以便它代表新的大小,然後還決定是否更新展開的循環以處理新的大小)。所以實際上,我會在結構體的定義點處聲明斷言,並且還*展開展開時的警告。 – 2009-08-21 19:47:29

1

這可能不會幫助,但如果你在C這樣做是爲了分派到相應的循環在編譯時的能力++,你可以使用模板來使編譯器:

template <std::size_t SizeOfEntry> 
void process_entry_loop(...) 
{ 
    // ... the nonoptimized version of the loop 
} 

template <> 
void process_entry_loop<12>(...) 
{ 
    // ... the optimized version of the loop 
} 

// ... 

process_entry_loop<sizeof(Entry)>(...); 
+2

好主意,但是將sizeof(Entry)作爲模板參數會更清晰,並且專門用於12. – 2009-08-21 18:21:40

+0

提示:我喜歡這個! – fbrereto 2009-08-21 18:33:38

1

另外兩個方法可以讓人想起 - 或者編寫一個小應用程序來編寫展開的循環,或者使用Duff's device上的變體以及結構的預期大小。

+1

任何現代編譯器生成的代碼都比Duff的設備慢,這不是一個很好的編譯器。 – 2009-08-21 05:48:29

+0

這不取決於它是如何偏執的代碼大小?爲了好玩,編譯器不一定會將代碼的大小乘以12。沒有分析器輸入,優化器無法知道哪些循環值得付費代碼大小以獲得速度。展開所有內容都會產生大量代碼,大量的icache遺漏以及速度減慢。另一方面,您可以智能地選擇展開哪些循環(或者更智能地使用可以使用探查器數據優化的編譯器)。除非你的意思是Duff's Device現在已經過時了,因爲我不知道更新,更好的技巧。 – 2009-08-21 23:28:14