7

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

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

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

#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. 文档,不适用于大多数用户。
4

2 回答 2

7

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

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

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

我在struct这里使用未标记的

  • 有一个类型,所以真的没有临时产生
  • 具有唯一的类型,这样表达式可以在代码中的任何位置重复而不会引起冲突
  • struct触发使用 VLA,从 C99 开始,在 a 中是不允许的:

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

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

编辑:似乎 gcc 接受 VLAstruct作为扩展,甚至没有警告它,即使我明确地说-std=c99. 这对他们来说真的是个坏主意。

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

于 2012-02-14T09:46:27.907 回答
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 在评论中所说,在此解决方案中不能使用解析为负整数的整数常量表达式,因为位域宽度不能为负。

于 2012-02-14T09:48:35.183 回答