编译时硬限制参数值

Hard limit parameter value at compile time

假设我们有一个数组:

struct some array[] = {A, B, C, D};

假设我们有一个函数,给定一个参数索引,用那个值做一些事情:

void sfrugula(size_t index){
    do_it( &array[index] );
}

现在,我们知道我们希望几乎总是使用静态值而不是变量调用该函数,例如:

sfugula(10);

有没有办法检查编译时有没有溢出,有没有报错?

重点是在编译时硬限制和检查参数(如果可能的话),因为这可能不仅适用于数组,甚至可能仅适用于某些变量。

使用枚举(enum)。通过这种方式,您可以将变量的值限制为一组特定的整数。 请注意,枚举仅适用于整数,适用于相对较小的一组值。

即使定义和用法并排,编译器也不会检查数组索引。

int array[3];
array[10] = 0;

这是一个设计它的解决方案,但是很笨拙:

#include<stdio.h>

#define ARRMAX  3
#define INDEX   10

int main()
{
    int array[ARRMAX];
    #if INDEX >= ARRMAX
    #error Array index is out of range
    #endif
    array[INDEX] = 0;
    return 0;
}

编译器输出:

test.c(10) : fatal error C1189: #error :  Array index is out of range

但是您不能在函数内执行此操作,您必须在调用函数时执行此操作,并且知道函数的作用。最好在 运行 时间在你的函数中解决这个问题,然后你可以检查传递的静态值和变量值。

sfrugula函数内部,不,无法进行编译时检查。

C 使用非常简单的构建系统,编译器不关心这些事情。它实际上只是遍历您的代码,根据参数列表和其中的语句为每个函数吐出汇编程序。当然它可以进行这些类型的检查,但请记住,越界数组访问仍然是符合 C 代码的,并且除了强制遵守之外,大多数 C 编译器不会判断您可能喜欢如何使用或滥用该语言。

另一方面,预处理器提供了一些编译时验证的能力,但这并没有扩展到你想做的事情。所以你最好的选择是检查每个地方的范围 sfugula 被调用,或者添加一个 运行 时间检查,你只在调试模式下使用。但是,除非您的系统资源严重受限,否则通常最好在生产代码中包含错误检查和适当的故障机制。了解程序如何处理此类错误,即使您认为可以通过静态检查来减轻这种错误,也会使您的程序更加健壮。

这并没有直接回答问题,因为它没有提供在编译时进行检查的机制。但是,如果没有(明显的)方法来做到这一点,我会建议,除非您要构建一个将由第三方开发人员调用的库 API,否则您应该提供一种机制 运行-时间范围检查,并且可以在您完成详尽测试后对发布版本禁用。

(对于 public API,离开范围签入非常重要。)

您可以将对函数的调用包装在一个宏中,对于发布版本可以禁用它。

#ifdef DEBUG
    #define SAFE_sfrugula(index) do\
    {\
        if(index < ((sizeof array) / (sizeof array[0]))) \
            sfrugula(index);\
        else\
        {
            printf("sfrugula index out of range on line %d",__LINE__);\
            exit(-1);\
        }\
    } while(0)
#else
    #define SAFE_sfrugula(index) sfrugula(index)
#endif

这是我的解决方案,它使用枚举和 X 宏:

我创建了一个外部文件 "ports.h"。 X() 左边的元素是 "nice name used by user",右边是对应的 REAL 值(或者它的唯一部分,如下所示)

#ifdef DDRA && PORTA && PINA
X(A, AAA)
#endif
#ifdef DDRB && PORTB && PINB
X(B, BBB)
#endif

然后在另一个文件中我声明了枚举 (A, B, ....) 和指向实际元素的指针的并行数组( 在这种情况下是 my_AAA, my_BBB, 等等...)

#define SEP ,
#define X(a, b) a SEP
enum PORTS {
    #include "ports.h"
};
#undef X

/* here we initialize the array of structure */
#define X(a, b) &my_##b SEP
static const uint8_t *array[] =
{
    #include "ports.h"
};
#undef X
#undef SEP

最后只需更改我们的函数以使用枚举

void sfrugula(enum PORTS p){
    do_it( &array[p] );
}

我必须做的唯一不同的事情是在使用函数时使用枚举而不是数值,但是如果我忘记了,编译器不会发出警告;现在我正在寻找一些 typedef magic 来实现这一点(在 C++ 中,如果你使用“-Wenum-compare”就已经很好了 如果使用“-Wall”则默认为 [=14] =]