【问题标题】:How to check if a parameter is an integral constant expression in a C preprocessor macro?如何检查参数是否是 C 预处理器宏中的整数常量表达式?
【发布时间】:2012-02-14 09:33:20
【问题描述】:

我目前正在清理现有的 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. 文档,不适用于大多数用户。

【问题讨论】:

  • 为了道德化,我认为这个问题是一个很好的例子,说明为什么宏一开始就不好。它有点像高度成瘾的药物。你知道它们很糟糕,一旦你开始使用它们,你就无法摆脱。你会发现自己越来越需要它们。看看这里真正需要什么的字里行间:“有没有人知道一个宏来修复这个宏,它调用一个宏。”
  • 即使这样我也找不到任何不好的宏调用另一个宏,这不是重点。上面的 complex_algorithm 代表你可能想要的任何东西。除了内联函数之外还有一个宏可以在直接初始化中使用它,同时有机会验证参数。

标签: c macros c-preprocessor constant-expression


【解决方案1】:

您可以使用任何需要常量整数表达式的表达式,然后将其优化出来。

#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 也抱怨它。

【讨论】:

  • 我认为它可以与 C99 VLA 一起编译。但是struct { int needs_constant:X; } 应该可以工作;可变长度位域仍然是不可能的。
  • @MSalters,不,它不能用 VLA 编译,struct 中不允许使用它们。
  • 结构体中允许使用 VLA,但只能作为最后一个成员。
  • @Lutz,不,不允许。也许您会想到灵活的数组成员?这些是不同的动物。它们的语法为空[],并且没有关于该灵活部分的编译时大小信息。
  • 对结果类型很好地使用条件运算符!
【解决方案2】:
#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 在评论中所说,解析为负整数的整数常量表达式不能用于此解决方案,因为位域宽度不能为负数。

【讨论】:

  • 不,在 C99 中不起作用,因为 int[x] 只是一个 VLA。如果在静态初始化的上下文中使用它也不起作用,因为assert 是运行时检查。
  • C99 我认为允许这样做。检查 MSalters 评论
  • @ouah,很好的尝试,但还是不行我的解决方案也是如此,我会更新它。
  • @JensGustedt 用于 VLA 和静态初始化我在写第一次尝试时就想到了它,但否定的好点我没有想到。
猜你喜欢
  • 2018-09-03
  • 2011-04-08
  • 1970-01-01
  • 2023-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-24
相关资源
最近更新 更多