C 编译器检查 typedef'ed void *



我们有一个匿名类型,typedefed到void *,这是一个API的句柄(所有代码在C11中)。它故意是void *,因为它指向的变化取决于我们编译的平台,我们也不希望应用程序尝试解引用它。在内部,我们知道它应该指向什么,并适当地强制转换它。这很好,代码是公开的,我们已经使用它很多年了,它不能被修改。

问题是我们现在需要引入另一个句柄,我们不希望用户混淆这两个句柄,我们希望编译器在传递错误句柄给函数时抛出错误。然而,到目前为止,我尝试过的所有C编译器(GCC, Clang, MSVC)的所有版本都不在乎;他们知道底层类型是void *,所以任何事情都可以(这是-Wall-Werror)。换句话说,我们的typedef没有取得任何成就,我们还不如直接使用void *。我也尝试过Lint和CodeChecker,他们似乎也不关心(尽管你可能会质疑我的配置)。请注意,我不能使用-Wpedantic,因为我们包含第三方代码,这将无法飞行。

我已经尝试使新的东西一个特定的typedefed指针,而不是void *,但这并不能完全解决问题,因为编译器仍然很高兴调用者将新的特定typedefed指针传递到期望现有句柄typedef的现有函数中。

是否有(a)一种方法来构建一个新的匿名句柄,这样编译器将不允许它被传递给现有的函数或(b)一个检查器,我们可以应用来挑选问题,至少在我们自己使用这些api ?

下面是一些代码来说明这个问题:
#include <stdlib.h>
typedef struct {
int contents;
} existingThing_t;
typedef void *anonExistingHandle_t;

typedef struct {
char contents[10];
} newThing_t;
typedef void *anonNewHandle_t;
typedef newThing_t *newHandle_t;

static void functionExisting(anonExistingHandle_t handle)
{
existingThing_t *pThing = (existingThing_t *) handle;

// Perform the function
(void) pThing;
}
static void functionNew(anonNewHandle_t handle)
{
newThing_t *pThing = (newThing_t *) handle;

// Perform a new function
(void) pThing;
}
int main() {
anonExistingHandle_t existingHandle = NULL;
anonNewHandle_t newHandleA = NULL;
newHandle_t newHandleB = NULL;
functionExisting(existingHandle);
functionNew(newHandleA);
// These should result in a compilation error
functionExisting(newHandleA);
functionNew(existingHandle);
functionExisting(newHandleB);
return 0;
}

是否有一种方法可以构造一个新的匿名句柄,使编译器不允许将其传递给现有的函数

是的,使用不能隐式转换为void *的类型。使用结构

typedef struct {
struct newThing_s *p;
} anonNewHandle_t;

无论如何,你的设计是有缺陷的,并且禁用了所有静态编译器检查。不要使用void *,而是使用内部包含void *的结构体或结构体来启用编译检查。研究非常非常标准的FILE *是如何工作的。FILE不是void.

不要使用typedef指针。它们非常令人困惑。使用https://wiki.sei.cmu.edu/confluence/display/c/DCL05-C。+ + typedef + +非点源+类型+只有

我建议重写你的库,使你不使用void *和不使用typedef指针。

设计可能像下面这样:

// handle.h
struct handle_s;
typedef struct {
struct handle_s *p;
} handle_t;
handle_t handle_init(void);
void handle_deinit(handle_t t);
void handle_do_something(handle_t t);
// handle.c
struct handle_s {
int the_stuff_you_need;
};
handle_t handle_init(void) {
return (handle_t){
.p = calloc(1, sizeof(struct handle_s))
};
}
void handle_do_something(handle_t h) {
struct hadnle_s *t = h->p;
// etc.
}
// anotherhandle.h
// similar to above
typedef struct {
struct anotherhandle_s *p;
} anotherhandle_t;
void anotherhandle_do_something(anotherhandle_t h);
// main
int main() {
handle_t h = handle_new();
handle_do_something(h);
handle_free(h);
anotherhandle_do_something(h); // compiler error
}

相关内容

最新更新