在C语言中是否有一种方法可以强制函数调用者向函数发送一个常量值(如果不是,则期望编译错误)?
例如,考虑以下简单功能:
int foo(int x, int y)
{
assert( x < 10);
return x+y;
}
如果我能以某种方式告诉编译器在所有情况下均应以恒定值发送x,则可以在编译时评估断言(即使实际上仅在运行时调用了断言)。有办法吗?
仅说明原因-在低资源系统中,在编译时对事物进行评估可以显着降低软件足迹和执行时间。
谢谢。
答案 0 :(得分:3)
首先,这是一个完全标准的C11解决方案:
#include <stdint.h>
#include <assert.h>
#define is_integral_constant_p(c) (_Generic(0 ? (void*)(uintptr_t)((c) - (c)) : (int*)(0), \
int*: 1, \
default: 0))
#define foo(x, y) (is_integral_constant_p(x) ? real_foo : error_foo_x_is_not_a_constant_expression)(x, y)
extern int error_foo_x_is_not_a_constant_expression(int, int);
int real_foo(int x, int y)
{
assert(x < 10);
return x+y;
}
int main(void)
{
static_assert(is_integral_constant_p(5), "5 is not a constant expression?");
foo(5, 1);
// int x = 0;
// foo(x, 1); // This will not link
}
魔术是条件表达式的工作方式。如果操作数之一为空指针常量,则结果的类型为另一个指针参数的类型。否则,如果一个参数为void*
,则结果为void*
。由于(void*)(0)
是空指针常量,因此仅当(c) - (c)
是等于0
的整数常量表达式时,第二个操作数才是空指针常量。如果c
是整数常量表达式,则会发生这种情况。
_Generic
只是根据结果的类型选择一个常量,在实际情况下,我们希望该常量为int*
,否则为void*
。此外,宏本身会求值为整数常量表达式(因此甚至可以在assert
中使用)。
以上内容是对Linux内核中使用的技术的一种改编(如here所述),仅不对C使用GNU特定扩展。仅使用C11功能。
现在,我们将foo
实现为对x
进行检查的宏,然后将其转发到进行计算的“ real_foo”,或者转发到已声明但未定义的函数。因此,如果x
不是常量,程序将无法链接。
here直播。
对于您的特定情况,您可以完全删除断言并将支票移到foo
宏中。看起来像这样:
#define foo(x, y) (is_integral_constat_p(x) && (x < 10) ? real_foo : \
error_foo_x_is_not_a_constant_expression_or_more_than_10)(x, y)
该宏结合短路评估为我们提供了所需的行为。如您所见here。
在这一点上,我认为我应该警告您。该代码可能不是那么直接以保护代码审查。因此,请仔细考虑您可能获得的任何性能提升是否值得解释。