'Typesafe'枚举,以避免检查C中的数组索引

时间:2018-06-29 07:14:59

标签: c arrays

使用“类型安全”枚举来限制函数的参数以避免检查数组索引是否是一种合适的方法吗?

我有一个模块,可将其实例的数据保存在Array中。实例的数据应使用索引从模块外部访问。将有几个接口函数,我想避免几个if语句。

以下示例:

// In myInstanceModule.h

typedef struct { enum { FIRST, SECOND } index_e; } instance_tst;
#define FIRST_INSTANCE  (instance_tst){ FIRST }
#define SECOND_INSTANCE (instance_tst){ SECOND }

void instance_init_v();
void instance_print_v(instance_tst instance);

// In myInstanceModule.c

#define MEMBER_COUNT 2

typedef struct myArray {
    int myValue; 
}myArray_tst;

static myArray_tst myMembers_ast[MEMBER_COUNT];

void instance_init_v() {
    for (int i = 0; i < MEMBER_COUNT; i++)
    {
        myMembers_ast[i].myValue = i * 10;
    }
}

void instance_print_v(instance_tst instance) {
    printf("Value of this instance is: %d \n", myMembers_ast[instance.index_e].myValue);
}

// In main.c

#include myModule.h
int main(void)
{
    int test = 1234;
    instance_init_v();

    instance_print_v(FIRST_INSTANCE);       // ok
    instance_print_v(SECOND_INSTANCE);      // ok
    //instance_print_v((instance_tst)2);    // does not compile
    //instance_print_v(test);               // does not compile
    //instance_print_v(1);                  // does not compile
    //instance_print_v(NULL);               // does not compile
}

一个文件中的示例:https://repl.it/repls/QuarrelsomeDotingComputation

3 个答案:

答案 0 :(得分:1)

不幸的是,C对枚举非常宽容。枚举只是符号常量的列表,您始终可以使用基础类型来欺骗它。在这里

instance_print_v((instance_tst){2});    // does compile

可以很好地编译(甚至没有警告),并使访问超出您试图阻止的数组末尾。

答案 1 :(得分:1)

这种方法不会阻止某人使用复合文字,例如

instance_print_v(((instance_tst){2}));

宁可

void instance_print_v(size_t index){
    if(index < sizeof(myMembers_ast)/sizeof(myMembers_ast[0]))
    {
      printf("Value of this instance is: %d \n", myMembers_ast[index].myValue);
    }
    else
    {
      printf("Value of this instance is: undefined");
    }
}

答案 2 :(得分:1)

使您的代码适应我在这里发明的技巧:How to create type safe enums?,然后得到的最终结果是这样的:

#include <stdio.h>

// In myInstanceModule.h

typedef enum
{
  FIRST,
  SECOND
} instance_tst;

typedef union
{
  instance_tst FIRST;
  instance_tst SECOND;
} typesafe_instance_t;

#define instance_assign(var, val) _Generic((var), \
  instance_tst: (var) = (typesafe_instance_t){ .val = val }.val )

void instance_init_v();
void instance_print (instance_tst instance); // the actual function

// type-safe wrapper:
#define instance_print_v(val) instance_print( instance_assign((instance_tst){0}, val) )

// In myInstanceModule.c

#define MEMBER_COUNT 2

typedef struct myArray {
    int myValue; 
}myArray_tst;

static myArray_tst myMembers_ast[MEMBER_COUNT];

void instance_init_v() {
    for (int i = 0; i < MEMBER_COUNT; i++)
    {
        myMembers_ast[i].myValue = i * 10;
    }
}

void instance_print (instance_tst instance) {
    printf("Value of this instance is: %d \n", myMembers_ast[instance].myValue);
}

// In main.c

int main(void)
{
    int test = 1234;
    instance_init_v();

    instance_print_v(FIRST);       // ok
    instance_print_v(SECOND);      // ok

    //instance_print_v((instance_tst)2);    // does not compile
    //instance_print_v(test);               // does not compile
    //instance_print_v(1);                  // does not compile
    //instance_print_v(NULL);               // does not compile
    //instance_print_v((instance_tst){20}); // does not compile
}

不幸的是,这也会阻止instance_tst变量传递给函数,但据我了解,这不是问题。