是否值得更改 C 声明以增加类型安全性?
Is it worthwhile changing a C declaration to add type safety?
我想知道覆盖 header 声明以设置特定类型是否合理或值得。优先于 void * 之类的东西,它不增加类型安全性。
例如,如果您有一个将传感器读数添加到循环缓冲区的通用存储函数:
int add_reading(void *);
为了通用,函数必须定义为 void *。但是,在 header 文件中,您可以将函数声明为:
int add_reading(my_reading_t *);
这将在 void 指针上增加一定程度的类型安全性。在通用 header 中,您可以使用默认为 void 的 #define 设置类型。因此覆盖类型可以在 #include.
之前定义
这似乎是一种不必要的 hack,但对于不透明指针也可以争论同样的问题——使用 opaque_type_t * 而不是 void *。但这至少是定义的行为。我想知道的是这种混乱是否会调用 UB(未定义的行为)?
你的建议看起来不是个好主意。如果你想提高类型安全性,如果你试图传入错误的类型就无法编译,你可以尝试使用 C11 的 _Generic
.
int add_reading (void *);
#define ADD_READING_HELPER(X) _Generic((X), \
my_reader_t *: add_reading((X)) \
)
int main(void) {
my_reader_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
将阻止程序编译,因为它不会'对如何处理该类型有任何规则。
用 int add_reading(void *)
声明的函数与用 int add_reading(my_reading_t *)
定义的函数不兼容。根据 C 2018 6.5.2.2 9:
,C 标准不会定义使用用前者声明的标识符(或具有该类型的其他函数指示符)调用用后者定义的函数的行为
If the function is defined with a type that is not compatible with the type (of the expression) pointed to by the expression that denotes the called function, the behavior is undefined.
根据 6.7.6.1 2:
For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.
显然,参数类型void *
和my_reading_t *
不是指向兼容类型的指针(假设my_reading_t
是结构类型,不是void
的别名) .
根据 6.7.6.3 15:
For two function types to be compatible,… corresponding parameters shall have compatible types…
我想知道覆盖 header 声明以设置特定类型是否合理或值得。优先于 void * 之类的东西,它不增加类型安全性。
例如,如果您有一个将传感器读数添加到循环缓冲区的通用存储函数:
int add_reading(void *);
为了通用,函数必须定义为 void *。但是,在 header 文件中,您可以将函数声明为:
int add_reading(my_reading_t *);
这将在 void 指针上增加一定程度的类型安全性。在通用 header 中,您可以使用默认为 void 的 #define 设置类型。因此覆盖类型可以在 #include.
之前定义这似乎是一种不必要的 hack,但对于不透明指针也可以争论同样的问题——使用 opaque_type_t * 而不是 void *。但这至少是定义的行为。我想知道的是这种混乱是否会调用 UB(未定义的行为)?
你的建议看起来不是个好主意。如果你想提高类型安全性,如果你试图传入错误的类型就无法编译,你可以尝试使用 C11 的 _Generic
.
int add_reading (void *);
#define ADD_READING_HELPER(X) _Generic((X), \
my_reader_t *: add_reading((X)) \
)
int main(void) {
my_reader_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
将阻止程序编译,因为它不会'对如何处理该类型有任何规则。
用 int add_reading(void *)
声明的函数与用 int add_reading(my_reading_t *)
定义的函数不兼容。根据 C 2018 6.5.2.2 9:
If the function is defined with a type that is not compatible with the type (of the expression) pointed to by the expression that denotes the called function, the behavior is undefined.
根据 6.7.6.1 2:
For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.
显然,参数类型void *
和my_reading_t *
不是指向兼容类型的指针(假设my_reading_t
是结构类型,不是void
的别名) .
根据 6.7.6.3 15:
For two function types to be compatible,… corresponding parameters shall have compatible types…