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

发布于 2025-01-04 22:58:16 字数 705 浏览 1 评论 0原文

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

I'm currently cleaning up an existing C-library to publish it shamelessly.

A preprocessor macro NPOT is used to calculate the next greater power of two for a given integral constant expression at compile time. The macro is normally used in direct initialisations. For all other cases (e.g. using variable parameters), there is an inline function with the same function.

But if the user passes a variable, the algorithm expands to a huge piece of machine code. My question is:
What may I do to prevent a user from passing anything but an integral constant expression to my macro?

#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
}

What I already tried:

  1. Define the macro to #define NPOT(x) complex_algorithm(x ## u). It still works and throws a - even if hardly helpful - compiler error for variable parameters. Unless there is no variable like iu... Dirty, dangerous, don't want it.
  2. Documentation, didn't work for most users.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

寂寞花火° 2025-01-11 22:58:16

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

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

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

我在这里使用未标记的 struct 来

  • 获得类型,因此实际上不会生成
  • 具有唯一类型的临时变量,这样表达式可以在代码中的任何位置重复,而不会导致冲突
  • 触发 VLA 的使用,这从 C99 开始,不允许在 struct 中使用:

结构或联合的成员可以具有除
可变修改类型。

我使用三元 ?:1 作为选择表达式,以确保 : 始终评估其类型,但从未评估为一个表达。

编辑:看起来gcc接受struct内部的VLA作为扩展,甚至没有警告它,即使我明确地说-std=c99.他们这实在是一个坏主意。

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

You can use any expression that needs a constant integral expression and that will then be optimized out.

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

eventually you should cast the result of the sizeof to the appropriate integer type, so the return expression is of a type that you'd expect.

I am using an untagged struct here to

  • have a type so really no temporary is produced
  • have a unique type such that the expression can be repeated anywhere in the code without causing conflicts
  • trigger the use of a VLA, which is not allowed inside a struct as of C99:

A member of a structure or union may have any object type other than a
variably modified type.

I am using the ternary ?: with 1 as the selecting expression to ensure that the : is always evaluated for its type, but never evaluated as an expression.

Edit: It seems that gcc accepts VLA inside struct as an extension and doesn't even warn about it, even when I explicitly say -std=c99. This is really a bad idea of them.

For such a weird compiler :) you could use sizeof((int[X]){ 0 }), instead. This is "as forbidden" as the above version, but additionally even gcc complains about it.

ぶ宁プ宁ぶ 2025-01-11 22:58:16
#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 在注释中所说,解析为负整数的整数常量表达式不能在此解决方案中使用,因为位字段宽度不能为负。

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

This will give a compile error if x is not a integral constant expression.

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

Note that this solution does not work for initializing a static variable:

static int a = INTEGRAL_CONST_EXPR(2 + 3);

will trigger a compile error because of an expression with , is not a constant expression.

As @JensGustedt put in the comment, an integral constant expression resolving to a negative integer number cannot be used in this solution as bit-field width cannot be negative.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文