是否可以强制函数参数在C中保持不变?

时间:2018-10-15 14:46:23

标签: c function const constants

在C语言中是否有一种方法可以强制函数调用者向函数发送一个常量值(如果不是,则期望编译错误)?

例如,考虑以下简单功能:

int foo(int x, int y)
{
    assert( x < 10);
    return x+y;
}

如果我能以某种方式告诉编译器在所有情况下均应以恒定值发送x,则可以在编译时评估断言(即使实际上仅在运行时调用了断言)。有办法吗?

仅说明原因-在低资源系统中,在编译时对事物进行评估可以显着降低软件足迹和执行时间。

谢谢。

1 个答案:

答案 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

在这一点上,我认为我应该警告您。该代码可能不是那么直接以保护代码审查。因此,请仔细考虑您可能获得的任何性能提升是否值得解释。