带常量数组的C编译时断言

带常量数组的C编译时断言,c,arrays,assert,static-assert,C,Arrays,Assert,Static Assert,我有一个非常大的常量数组,它在编译时初始化 typedef enum { VALUE_A, VALUE_B,...,VALUE_GGF } VALUES; const int arr[VALUE_GGF+1] = { VALUE_A, VALUE_B, ... ,VALUE_GGF}; 我想验证数组是否已正确初始化,例如: if (arr[VALUE_GGF] != VALUE_GGF) { printf("Error occurred. arr[VALUE_GGF]=%d\n",

我有一个非常大的常量数组,它在编译时初始化

typedef enum {
VALUE_A, VALUE_B,...,VALUE_GGF
} VALUES;

const int arr[VALUE_GGF+1] = { VALUE_A, VALUE_B, ... ,VALUE_GGF};
我想验证数组是否已正确初始化,例如:

if (arr[VALUE_GGF] != VALUE_GGF) {
    printf("Error occurred. arr[VALUE_GGF]=%d\n", arr[VALUE_GGF]);
    exit(1);
}
我的问题是我想在编译时验证这一点。我在以下线程中阅读了C中的编译时断言:。但是,这里提供的解决方案建议定义一个数组,使用负值作为编译错误的大小:

#define CASSERT(predicate, file) _impl_CASSERT_LINE(predicate,__LINE__,file)
#define _impl_PASTE(a,b) a##b
#define _impl_CASSERT_LINE(predicate, line, file) \
   typedef char _impl_PASTE(assertion_failed_##file##_,line)[2*!!(predicate)-1];
和使用:

CASSERT(sizeof(struct foo) == 76, demo_c);
提供的解决方案不适用于我,因为我需要验证我的常量数组值,C不允许使用常量数组值初始化数组:

int main() {
   const int i = 8;
   int b[i];         //OK in C++
   int b[arr[0]];    //C2057 Error in VS2005

有什么办法吗?其他编译时间断言?

< P>问题是在C++中编译时常量表达式有以下限制(5.19个常量表达式):

整型常量表达式只能涉及整型或枚举类型的文字(2.13)、枚举数、常量变量或静态数据成员(使用常量表达式初始化)(8.5)、整型或枚举类型的非类型模板参数以及sizeof表达式。浮动文字(2.13.3)仅在强制转换为整型或枚举类型时才会出现。只能使用到整型或枚举类型的类型转换。特别是,除sizeof表达式外,不得使用函数、类对象、指针或引用,也不得使用赋值、递增、递减、函数调用或逗号运算符

请记住,数组索引表达式实际上只是伪装的指针算术(
arr[0]
实际上是
arr+0
),指针不能用于常量表达式,即使它们是指向常量数据的指针。因此,我认为使用编译时断言来检查数组内容是不走运的

在编译时,这些表达式可以比C++更有限,在编译时使用这些表达式。p>

但是,C++的复杂性,可能有人能想出一个外箱的解决方案。

< P>问题是,在C++中编译时常量表达式有以下限制(5.19个常量表达式):

整型常量表达式只能涉及整型或枚举类型的文字(2.13)、枚举数、常量变量或静态数据成员(使用常量表达式初始化)(8.5)、整型或枚举类型的非类型模板参数以及sizeof表达式。浮动文字(2.13.3)仅在强制转换为整型或枚举类型时才会出现。只能使用到整型或枚举类型的类型转换。特别是,除sizeof表达式外,不得使用函数、类对象、指针或引用,也不得使用赋值、递增、递减、函数调用或逗号运算符

请记住,数组索引表达式实际上只是伪装的指针算术(
arr[0]
实际上是
arr+0
),指针不能用于常量表达式,即使它们是指向常量数据的指针。因此,我认为使用编译时断言来检查数组内容是不走运的

在编译时,这些表达式可以比C++更有限,在编译时使用这些表达式。p>
但是考虑到C++的复杂性,也许有人可以想出一个开箱即用的解决方案。

您可以将您的断言表示为一个属性,以便使用静态分析器进行检查,并让分析器进行检查。这具有您想要执行的操作的一些属性:

  • 属性是在源代码中编写的

  • 它不会污染生成的二进制代码

但是,它不同于编译时断言,因为它需要在程序上运行一个单独的工具来进行检查。也许这是对您试图执行的编译器的健全性检查,在这种情况下,这没有帮助,因为静态分析器不检查编译器做什么,只检查它应该做什么。 补充:如果是为了QA,那么编写可以静态验证的“正式”断言现在非常流行。下面的方法与您可能听说过的.NET合同非常相似,但它适用于C

  • 您可能不太重视静态分析器,但正是循环和函数调用导致它们变得不精确。对他们来说,在初始化时,在任何这些事情发生之前,更容易清楚地了解正在发生的事情

  • 有些分析器宣称自己是“正确的”,也就是说,如果您编写的属性超出了它们的能力范围,它们不会保持沉默。在这种情况下,他们抱怨无法证明这一点。如果发生这种情况,在您确信问题出在分析器而不是阵列上之后,您将留在现在的位置,寻找另一种方法

以我熟悉的分析仪为例:

const int t[3] = {1, 2, 3};
int x;

int main(){

  //@ assert t[2] == 3 ;

  /* more code doing stuff */
}
运行分析器:

$ frama-c -val t.i
...
t.i:7: Warning: Assertion got status valid.
Values of globals at initialization 
t[0] ∈ {1; }
 [1] ∈ {2; }
 [2] ∈ {3; }
x ∈ {0; }
...
在分析仪的日志中,您可以得到:

  • 它认为globals的初始值是什么的版本
  • 以及它对你在/@评论中所写断言的解释。在这里,它对断言进行了一次检查并发现它是有效的
使用这种工具的人构建脚本,自动从日志中提取他们感兴趣的信息。
但是,作为一个否定的提示,我必须指出,如果您担心测试最终可能会被忘记,那么您还应该担心在代码修改后强制的静态分析器通过被忘记。

您可以将您的断言表示为一个属性,以便使用静态分析器进行检查,并让分析器进行检查。这具有您想要执行的操作的一些属性:

  • 属性是在源代码中编写的

  • 它不会污染生成的二进制代码

但是,它不同于编译时断言,因为它需要一个单独的工具在p上运行
typedef enum {VALUE_A, VALUE_B,...,VALUE_GGF} VALUES;
const int arr[] = { VALUE_A, VALUE_B, ... ,VALUE_GGF};
#define MY_ASSERT(expr)  {char uname[(expr)?1:-1];uname[0]=0;}
...
// If initialized count of elements is/are not correct, 
//   the compiler will complain on the below line
MY_ASSERT(sizeof(arr) == sizeof(int) * ARR_SIZE)
typedef enum {
    VALUE_A, VALUE_B,...,VALUE_GGF
} VALUES;
struct {
    static const VALUES elem0 = VALUE_A;
    static const VALUES elem1 = VALUE_B;
    static const VALUES elem2 = VALUE_C;
    ...
    static const VALUES elem4920 = VALUE_GGF;
    const int operator[](int offset) {return *(&elem0+offset);}
} arr;
void func() {
    static_assert(arr.elem0 == VALUE_A, "arr has been corrupted!");
    static_assert(arr.elem4920 == VALUE_GFF, "arr has been corrupted!");
}
#include <stdio.h>

typedef enum { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, NOF_WORKDAYS_IN_WEEK } WORKDAYS;

const char * const workday_names[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" };
const char * const (*p_workday_name_test)[NOF_WORKDAYS_IN_WEEK] = &workday_names;

const int workday_efforts[] = { 12, 23, 40, 20, 5 };
const int (*p_workday_effort_test)[NOF_WORKDAYS_IN_WEEK] = &workday_efforts;

main ()
{
  WORKDAYS i;
  int total_effort = 0;

  printf("Always give 100 %% at work!\n");

  for(i = MONDAY; i < NOF_WORKDAYS_IN_WEEK; i++)
  {
    printf(" - %d %% %s\n",workday_efforts[i], workday_names[i]);
    total_effort += workday_efforts[i];
  }
  printf(" %d %% in total !\n", total_effort);
}