2010-05-04 71 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