更改C声明以添加类型安全性值得吗?

时间:2019-02-18 21:48:16

标签: c type-conversion header-files void-pointers

我想知道为了设置特定类型而覆盖标头声明是否合理或值得。优先于void *之类的东西,它不会增加类型安全性。

例如,如果您具有将传感器读数添加到循环缓冲区的通用存储功能:

int add_reading(void *);

为了通用,必须将函数定义为void *。但是,在头文件中,您可以将函数声明为:

int add_reading(my_reading_t *);

,它将在void指针上增加一定程度的类型安全性。在通用标头中,您可以使用默认为void的#define设置类型。因此,可以在#include之前定义覆盖类型。

这似乎是不必要的骇客,但是对于不透明指针也可以争论相同-使用opaque_type_t *优先于void *。但这至少是明确的行为。我想知道的是,这种类型的混乱是否会调用UB(未定义的行为)?

2 个答案:

答案 0 :(得分:4)

int add_reading(void *)声明的函数与用int add_reading(my_reading_t *)定义的函数不兼容。根据C 2018 6.5.2.2 9:

,使用C语言不会定义使用由前者声明的标识符(或具有该类型的其他函数标识符)声明的由后者定义的函数的行为。
  

如果函数的类型与表示被调用函数的表达式所指向的类型(表达式的类型)不兼容,则行为是不确定的。

每6.7.6.1 2:

  

要使两个指针类型兼容,则两者必须具有相同的限定条件,并且都应是指向兼容类型的指针。

很显然,参数类型void *my_reading_t *不是指向兼容类型的指针(假定my_reading_t是结构类型,不是void的别名)。 / p>

根据6.7.6.3 15:

  

要使两种功能类型兼容,…相应的参数应具有兼容的类型…

答案 1 :(得分:2)

您提出的建议似乎是个坏主意。如果您要提升类型安全性,以便在尝试传递错误的类型时无法对其进行编译,则可以尝试使用C11的_Generic

int add_reading (void *);
#define ADD_READING_HELPER(X) _Generic((X), \
    my_reader_t *: add_reading((X)) \
)

int main(void) {
    my_helper_t good;
    printf("%d\n", ADD_READING_HELPER(&good)); // works because _Generic has a rule for dealing with (my_reader_t *)
    int bad;
    printf("%d\n", ADD_READING_HELPER(&bad)); // fails to compile because the _Generic does not have a rule for dealing with (int *)
}

int add_reading (void *arg) {
    // whatever the function does
}

基本上,_Generic允许您基于传递给表达式的控制类型来执行不同的操作,这些控制类型都是在编译时确定的。我们在这里所做的是为my_reader_t *创建一条规则,但没有其他类型,因此尝试将my_reader_t *以外的任何其他内容传递给_Generic将会阻止程序像编译程序那样编译关于如何处理该类型没有任何规则。