【问题标题】:Hard limit parameter value at compile time编译时的硬限制参数值
【发布时间】:2015-03-24 11:39:05
【问题描述】:

假设我们有一个数组:

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

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

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

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

sfugula(10);

有没有办法检查编译时没有溢出以及是否抛出错误?

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

【问题讨论】:

  • 最好拥有包含所有可用检查的“DEBUG”版本。
  • 在 C 中,没有对数组边界的继承检查。因此,被调用函数需要知道数组中的条目数并根据该限制检查参数。那将是在执行时。我知道没有办法在编译时检查。
  • 这是什么意思?没有编译时检查的调试版本意味着我必须使用测试程序检查所有可能的结果。编译检查更安全、更快。然后仍然应该运行测试以检查其他错误
  • @user3629249 我知道没有检查,我要的是在编译时有一些硬限制值的指令。我知道使用这种方法在运行时无法检查变量,与数组的使用只是一个示例。但是静态访问会更安全一些。

标签: c c-preprocessor buffer-overflow


【解决方案1】:

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

我创建了一个外部文件“ports.h”。 X() 的左边元素是“用户使用的好名字”,右边是对应的 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”,则默认情况下是女巫)

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 是的,我正在混合使用 x 宏和 ifdef。如果我有什么工作会发布
    【解决方案3】:

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

    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
    

    但是你不能在函数内做,你必须在你的函数被调用时做,知道函数做什么。在运行时在你的函数中解决这个问题要好得多,然后你可以检查传递的静态和变量值。

    【讨论】:

    • 与其依赖预处理器值来表示数组的大小,不如让预编译器提供更多帮助:#if(INDEX &gt;= ((sizeof array)/(sizeof array[0])))
    • 我通过了这个,如果我使用定义而不是函数(女巫将进行检查并调用函数),但在#define 中没有#if。好主意,但不可行
    【解决方案4】:

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

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

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

    【讨论】:

    • 我想找一个静态解决方案。这是因为系统的资源确实是有限的(64byte!内存,例如)
    • 你检查过你的工具链是否有静态分析器吗?否则你可以自己动手。最好将微控制器标签添加到您的问题中,否则您会得到很多关于添加运行时检查的答案。
    • 在嵌入式中,在这种情况下,在每个函数调用之前只看到一个断言是很正常的。
    • GCC 不做静态分析。然而 :) 对于断言我们将进入运行时,并且超出问题边界
    • 您可以在 C 中进行编译时断言。本质上,您只有一个评估为语法错误的宏。见this
    【解决方案5】:

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

    (对于公共 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
    

    【讨论】:

      猜你喜欢
      • 2016-03-26
      • 2015-08-19
      • 1970-01-01
      • 1970-01-01
      • 2015-07-27
      • 2012-06-22
      • 2012-07-05
      • 2021-06-11
      • 1970-01-01
      相关资源
      最近更新 更多