如何检查参数是否是C预处理器宏中的整数常量表达式?

时间:2012-02-14 09:33:20

标签: c macros c-preprocessor constant-expression

我目前正在清理一个现有的C库,以无耻地发布它。

预处理器宏NPOT用于计算编译时给定的整数常量表达式的下一个更大的2的幂。宏通常用于直接初始化。对于所有其他情况(例如,使用变量参数),存在具有相同功能的内联函数。

但是如果用户传递变量,算法会扩展为大量的机器代码。我的问题是: 我该怎么做才能防止用户向我的宏传递除了一个整数常量表达式之外的任何内容?

#define NPOT(x)   complex_algorithm(x)

const int c=10;
int main(void) {
    int i=5;

    foo = NPOT(5);   // works, and does everything it should
    foo = NPOT(c);   // works also, but blows up the code extremely
    foo = NPOT(i);   // blows up the code also
}

我已经尝试过:

  1. 将宏定义为#define NPOT(x) complex_algorithm(x ## u)。它仍然可以工作并抛出 - 即使几乎没有帮助 - 变量参数的编译器错误。除非没有像iu这样的变量......脏,危险,不要它。
  2. 文档,对大多数用户不起作用。

2 个答案:

答案 0 :(得分:6)

您可以使用任何需要常量积分表达式的表达式,然后进行优化。

#define NPOT(X)                                         \
 (1                                                     \
 ? complex_algorithm(X)                                 \
 : sizeof(struct { int needs_constant[1 ? 1 : (X)]; })  \
 )

最终你应该将sizeof的结果转换为适当的整数类型,因此返回表达式是你期望的类型。

我在这里使用未标记的struct

  • 有一个类型,所以真的没有临时产生
  • 具有唯一类型,以便表达式可以在代码中的任何位置重复,而不会导致冲突
  • 触发使用VLA,自C99起struct内不允许使用VLA:
  

结构或联合的成员可以具有除a之外的任何对象类型   可变修饰型。

我使用带有?:的三元1作为选择表达式,以确保始终对:的类型进行求值,但从不将其评估为表达式。

修改:似乎gcc接受struct内的VLA作为扩展,即使我明确说-std=c99,也不会对此发出警告。这对他们来说真是个坏主意。

对于这样一个奇怪的编译器:)你可以使用sizeof((int[X]){ 0 })。这与以上版本一样“被禁止”,但即便是gcc也会抱怨它。

答案 1 :(得分:0)

#define INTEGRAL_CONST_EXPR(x) ((void) sizeof (struct {int a:(x);}), (x))

如果x不是整数常量表达式,则会产生编译错误。

my_function(INTEGRAL_CONST_EXPR(1 + 2 + 3));    // OK
my_function(INTEGRAL_CONST_EXPR(1.0 + 2 + 3));  // compile error

请注意,此解决方案不适用于初始化静态变量:

static int a = INTEGRAL_CONST_EXPR(2 + 3);

将触发编译错误,因为,的表达式不是常量表达式。

当@JensGustedt放入注释时,解析为负整数的整数常量表达式不能用于此解决方案,因为位域宽度不能为负。