您当前的技术是正确的。尝试使用匿名(未标记)struct
失败了你想要做的事情 - 你必须暴露定义的细节struct
无处不在,这意味着您不再拥有不透明的数据类型。
In a comment https://stackoverflow.com/questions/26813097/how-do-i-typedef-an-implementation-defined-struct-in-a-generic-header/26814210?noredirect=1#comment42202359_26814210, 用户3629249 https://stackoverflow.com/users/3629249/user3629249 said:
头文件包含的顺序意味着存在对结构体的前向引用generic.h
文件;也就是说,在定义结构之前,就使用它。这不太可能编译。
对于问题中显示的标题,此观察结果是不正确的;对于样本来说是准确的main()
代码(在添加此响应之前我没有注意到)。
关键点是所示的接口函数接受或返回指向类型的指针gen_t
,这又映射到struct impl_t
指针。只要客户端代码不需要为该结构分配空间,或者取消引用指向结构的指针来访问该结构的成员,客户端代码就不需要知道该结构的详细信息。将结构类型声明为现有就足够了。您可以使用其中任何一个来声明struct impl_t
:
struct impl_t;
typedef struct impl_t gen_t;
后者还引入了别名gen_t
对于类型struct impl_t
。也可以看看C 标准的哪一部分允许编译此代码? https://stackoverflow.com/questions/12200096/which-part-of-the-c-standard-allows-this-code-to-compile and C标准是否认为存在一两个struct uperms此标头中的条目类型? https://stackoverflow.com/questions/11697705/does-the-c-standard-consider-that-there-are-one-or-two-struct-uperms-entry-typ
原本的main()
问题中的程序是:
int main (int argc, char *argv[]) {
int ret;
gen_t data;
ret = foo(&data);
…
}
此代码无法编译gen_t
作为不透明(非指针)类型。它可以与以下内容一起使用:
typedef struct impl_t *gen_t;
它不会编译:
typedef struct impl_t gen_t;
因为编译器必须知道结构有多大才能为其分配正确的空间data
,但是编译器无法根据不透明类型的定义知道该大小。 (看这是一个好主意吗typedef指点? https://stackoverflow.com/questions/750178/typedef-pointers-a-good-idea/750208#750208用于定义指向结构的指针。)
就这样main()
代码应该更像是:
#include "generic.h"
int main(int argc, char **argv)
{
gen_t *data = bar(argc, argv);
int ret = foo(data);
...
}
其中(对于本例)bar()
定义为extern gen_t *bar(int argc, char **argv);
,所以它返回一个指向不透明类型的指针gen_t
.
关于是否始终使用更好的观点存在分歧struct tagname
或使用typedef
为了名字。 Linux 内核是一个不使用typedef
机制;所有结构都明确struct tagname
。另一方面,C++ 不再需要显式的typedef
;写作:
struct impl_t;
在 C++ 程序中意味着名称impl_t
现在是一个类型的名称。由于不透明的结构类型需要标签(或者您最终会使用void *
对于所有事情,这出于多种原因都是不好的,但主要原因是您失去了所有类型安全性void *
;记住,typedef
引入了基础类型的别名,而不是新的不同类型),我用 C 编写的代码模拟了 C++:
typedef struct Generic Generic;
I avoid using the _t
suffix on my types because POSIX reserves the _t
for the implementation to use* (see also What does a type followed by _t represent? https://stackoverflow.com/questions/231760/what-does-a-type-followed-by-t-underscore-t-represent/231807#231807). You may be lucky and get away with it. I've worked on code bases where types like dec_t
and loc_t
were defined by the code base (which was not part of the implementation — where 'the implementation' means the C compiler and its supporting code, or the C library and its supporting code), and both those types caused pain for decades because some of the systems where the code was ported defined those types, as is the system's prerogative. One of the names I managed to get rid of; the other I didn't. 'Twas painful! If you must use _t
(it is a convenient way to indicate that something is a type), I recommend using a distinctive prefix too: pqr_typename_t
for some project pqr
, for example.
* See the bottom line of the second table in The Name Space http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_02_02 in the POSIX standard.