【问题标题】:Are compile time checks possible for value parameters in C99 or GCC/Clang?C99 或 GCC/Clang 中的值参数是否可以进行编译时检查?
【发布时间】:2020-06-30 19:30:06
【问题描述】:

我有一个函数,它的长度值不能大于 32,768。我想在编译时检查这个情况,因为在嵌入式系统中没有很好的方法来处理这个错误。此外,传入的长度值应该始终是编译时间常数,通常是sizeof()

这在 C99 或 GCC/Clang 扩展中是否可行?

类似这样的东西,显然无法编译,因为len 不是整数常量。

#include <assert.h>
void fucntion(const uint16_t len)
{
    _Static_assert(len <= 32768, "length parameter too large");
}

显然在这里使len const 没有预期的效果,并且_Static_assert 抱怨len 不是整数常量。

我曾考虑使用宏替换func(),然后调用它,但这有两个明显的缺点。宏定义不支持类型或返回值之类的东西,即你不能这样做

#define bool func(uint16_t len, const void *buffer)

正如 Nate Eldredge 指出的那样,有一个 GCC(和 Clang)extension 可以让您返回一个参数。

【问题讨论】:

  • 使用宏并检查调用端的长度。 integral constant -> 它是“整数”,如“整数常量表达式”。
  • @KamilCuk 我只是添加了关于宏选项的注释。它是“整数常量”,这是编译器可以在编译时计算的东西的术语,它们是 _Static_assert 宏所必需的。它不必是整数,可以是任何类型。
  • 我不太明白反对宏的说法。如果您的宏扩展包括对真实函数的调用,您将获得所有相同的好处。例如。 #define func(l, b) { _Static_assert(l &lt;= 32678,...); actual_function(l,b); }
  • @NateEldredge 它可以工作,但大多数 IDE 无法进行正确的提示或预编译检查。这不是世界末日,但如果有更好的选择就好了。可悲的是,我认为没有。
  • @user11567957:您可以使用statement expressions 解决这个问题,这是 gcc 和 clang 都支持的扩展。 #define func(l, b) ({ _Static_assert(l &lt;= 32678,...); actual_function(l,b); })

标签: gcc clang c99


【解决方案1】:

您可以使用 sizeof 和负数组的旧技巧编写静态断言,并在调用端扩展宏:

void function_real(const uint16_t len);

#define function(len) ((void)sizeof(char[len > 32768 ? -1 : 1]), function_real(len))

如果您将 C 与可变长度数组一起使用,则数组的 sizeof 将适用于不是整数常量表达式的表达式。您可以使用旧的位域技巧将len 强制为整数常量表达式以进行静态断言。如此相似,但更好:

 #define function(len) ((void)sizeof(struct{ int a:len > 32768 ? -1 : 1;}), function_real(len))

您可以像 glibc­_FORTIFY_SOURCE 那样做,并使用 gnu 内置的编译优化并启用优化:

void function_real(const uint16_t len);

static inline 
void function(const uint16_t len) {
     if (!__builtin_constant_p(len)) {
            extern __attribute__(( __error__ ( "Och no! len couldn't be evaulated at runtime" ) ))
            void compile_time_error(void);
            compile_time_error();
     } else if (len > 32768) {
            extern __attribute__(( __error__ ( "Och no! len is too small!" ) ))
            void compile_time_error2(void);
            compile_time_error2();
     }

     function_real(len);
}

【讨论】:

    【解决方案2】:

    注意一些小问题的一种可能性是:

    #define func(len, buffer)                                           \
    ({                                                                  \
        _Static_assert(len < 32768, "Length exceeds size of EEPROM");   \
        _func(len, buffer);                                             \
    })
    
    
    bool _func(uint16_t len, const void *buffer)
    { }
    

    感谢 Nate Eldredge。

    这种技术的缺点是 IDE 在浏览代码时只会显示有关宏的信息,因此例如您不会获得有关参数类型的提示。不过,您仍然会在编译时得到通常的检查和错误。

    【讨论】:

      猜你喜欢
      • 2014-12-04
      • 2011-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-15
      相关资源
      最近更新 更多