2010-05-04 98 views
2

在這裏,我們有一組C宏,用於使用預處理器執行位域操作,並且在Visual Studio中嘗試使用這些宏時會遇到警告。這個問題可以很容易地證明:位域掩碼計算宏

#define BITFIELD_WIDTHMASK(Width) \ 
    ((Width) >= 32 ? ~0x0ul : (1ul << (Width)) - 1) 

unsigned long foo(void) 
{ 
    return BITFIELD_WIDTHMASK(32); 
} 

與MSVC編譯此產生了警告:

test.c(12) : warning C4293: '<<' : shift count negative or too big, undefined behavior 

這是不是一個行爲問題 - <<運營商將不會在這種情況下使用,這應該在編譯時檢測到。但是,有沒有人有任何關於如何重寫宏以避免警告的建議?或者,如果失敗了,如何爲此重新設計宏接口?

預先感謝

回答

3

什麼:

#define BITFIELD_WIDTHMASK(Width) \ 
    ((Width) >= 32 ? ~0x0ul : (1ul << (Width % 32)) - 1) 

+0

工作,並且乾淨而緊密。到底是什麼我以後。謝謝! – 2010-05-04 22:37:28

+0

當寬度是常數時,這是有效的。但是,如果它是一個變量,那麼它的運行效率可能會低於一個班次,如果這很重要的話。仍然;涵蓋了所有的基礎。 +1。 – Clifford 2010-05-04 22:48:54

3
#define BITFIELD_WIDTHMASK(Width) (((0x80000000ul >> (32-Width)) << 1) - 1) 

或者處理的0寬度以及請求...

#define BITFIELD_WIDTHMASK(Width) \ 
    ((Width) >= 32 \ 
    ? ~0x0ul \ 
    : (((1ul << ((Width)/2)) << ((Width)/2)) << ((Width)&1)) - 1) 
+0

它解決了所述的問題,但給出'0'的'Width'參數時也有類似的問題。 – 2010-05-04 21:14:39

+0

#define BITFIELD_WIDTHMASK(Width)((Width == 0)?0:(Width == 1)?1:(Width == 2)?3:(Width == 3)?7:(Width == 4) ?15:... ;-) – 2010-05-04 21:22:57

+0

@Aidan Cully:你想要BITFIELD_WIDTHMASK(0)的結果是什麼?因爲這導致0xffffff(即與BITFIELD_WIDTHMASK(32)相同)。你的回答爲零。但是也許零寬度沒有任何用處。 – Clifford 2010-05-04 21:57:41

1

預處理不是編譯器計算表達式時寬度是文字常量,並對雙方表達評價雙方都太愚蠢了。我猜是因爲它根本不處理它,而是插入一個帶有常量操作數的?:表達式!

如果不需要零寬度,以下是1到32個工作簡單化:

#define BITFIELD_WIDTHMASK(Width) (~0ul >> (32-(Width))) 

在我看來,如果你知道這個寬度是零,(也許是爲了禁用功能),如果使用文字零常量,這是隱含的,只使用零而不是調用宏是合理的。

+0

這看起來很可能會像OP的定義一樣導致僞造警告,只是在一組不同的寬度值上(OP的代碼是正確的C,只是由於某種原因,編譯器無法看到該轉換永遠不會當寬度太大時評估)。 – caf 2010-05-04 22:18:15

+0

您會注意到我原來的解決方案有這樣的限制,但這並沒有阻止編譯器警告無效的轉換。我只是用我提到的編譯器試過這個版本的宏,它會產生相同的警告。 – 2010-05-04 22:23:22

+0

@Aidan Cully:是的,我剛剛意識到,我的測試錯誤:問題在於,即使寬度是常數,pre =處理器仍然會評估?中的右側操作數表達式。我修改了帖子(這可能會使有效評論過時,但只是忽略了問題!) – Clifford 2010-05-04 22:43:20