不透明数据类型在C中没有不透明指针

我想创建一个opaque类型,同时仍然允许该类型的用户通过键入来实例化它,例如在堆栈上

struct Foo obj; /*...*/ Foo_init(&obj); Foo_do_something(&obj, param); /*...*/ 

通常的不透明指针方法不允许此示例的第一行。 作为一种解决方法,我将公共头文件中的公共但不透明的“数据”数组放在固定大小的文件中。 这似乎适用于一些例子,但我有点不确定两点:

  1. 像在Foo_get_bar和Foo_set_bar中那样转换数据数组的地址是否安全? 这在我的测试中运行正常,但看起来有问题。

  2. 如果FOO_DATA_SIZE保持不变,那么在用户代码中期望ABI兼容性是否合理?


main.c(示例用户代码)

 #include  #include  #include "foo.h" int main() { struct Foo foo; Foo_init(&foo); Foo_set_bar(&foo, INT_MAX); int bar = Foo_get_bar(&foo); printf("Got bar: %d\n", bar); } 

foo.h(公共标题)

 #pragma once #include  #define FOO_DATA_SIZE (64) struct Foo { uint8_t data[FOO_DATA_SIZE]; }; void Foo_init(struct Foo *f); void Foo_set_bar(struct Foo *f, int barval); int Foo_get_bar(struct Foo *f); 

foo.c(实施)

 #include "foo.h" #include  #include  #include  typedef int64_t bar_t; struct Foo_private { bar_t bar; }; _Static_assert(sizeof(struct Foo_private) data, &foodata, sizeof(struct Foo_private)); } void Foo_set_bar(struct Foo *foo, int barval) { struct Foo_private *foodata = (void*)&(foo->data); foodata->bar = (bar_t)barval; int stored = (int)foodata->bar; if (stored != barval) { fprintf(stderr, "Foo_set_bar(%"PRId64"): warning: bar rounded to %"PRId64"\n", (int64_t)barval, (int64_t)stored); } } int Foo_get_bar(struct Foo *foo) { struct Foo_private *foodata = (void*)&(foo->data); bar_t bar = foodata->bar; return (int)bar; } 

我已经查看了这些post中的信息:

为什么不应该以这种方式隐藏结构实现呢?

静态分配不透明数据类型

以及评论。 我想我有一个有效的答案:我改用了一个不透明的指针类型,但我现在正在暴露一个函数调用,告诉用户它有多大,以便他可以调用alloca或malloc或其他什么来分配空间。 基本上要求是分配由用户执行,而不是执行。

修改标题:

 #pragma once #include  struct Foo; typedef struct Foo Foo; void Foo_init(Foo *f); void Foo_set_bar(Foo *f, int barval); int Foo_get_bar(Foo *f); size_t Foo_data_size(void); #define Foo_alloca() alloca(Foo_data_size()) #define Foo_new() malloc(Foo_data_size()) #define Foo_delete(PTR) do { \ free(PTR); \ PTR = NULL; \ } while(0) 

修改的实现定义:

 typedef int64_t bar_t; struct Foo { volatile bar_t bar; }; void Foo_init(struct Foo *foo) { struct Foo foodata; foodata.bar = (bar_t)0; memcpy(foo, &foodata, Foo_data_size()); } size_t Foo_data_size() { return sizeof(struct Foo); } //... 

然后在用户代码中,使用具有Foo_data_size()提供的大小的alloca或使用便捷宏。

这种方法消除了固定大小的限制,并希望解决所提到的对齐问题。

私有结构声明中的单词volatile是不相关的。 如果不以这种方式声明,gcc至少在win32上试图优化我在不兼容的表示中存储某些值的约束检查。

用法示例:

 #include "foo.h" //... { Foo *foo = Foo_alloca(); Foo_init(foo); Foo_set_bar(foo, INT_MAX); int bar = Foo_get_bar(foo); printf("Got bar: %d\n", bar); }