我是否正确假设无法正向声明库的不透明指针类型?
关于前向声明和不透明类型有很多问题,但大多数似乎是从图书馆作者的角度出发,或者是人们试图使用不带指针的不完整类型或者其他类似的。
我正在使用一个接口/接受FOO *
指针的库。 我想确认我不能(或不应该)以某种方式在我的头文件中定义FOO
或FOO *
(它定义了一个带有FOO *
成员的结构)。
我知道我可以在我的头文件和.c文件中只#include
,但由于这只是一个学习项目,我想澄清一下。 (一方面,似乎前向声明似乎是可能的,因为我的结构成员只是一个指针,因此它的大小在不知道FOO
是什么的情况下是已知的 – 但另一方面,我不知道它是否是当库已经在那里时,有效/智能地向FOO
typedef
一些东西。)
提前致谢!
假设您永远不需要取消引用指针,那么如果您知道它的struct标记名称,则可以使用opaque类型指针:
typedef struct FOO FOO;
您现在可以创建FOO *
变量,并使用它们。 您可能会从头文件中找到结构标记,但您应该知道库所有者可以随时更改它。
通常最好包含官方标题,但如果大多数代码没有访问实际的库,只需将句柄传递给库返回的内容,就可以避免包含实际标题的“成本”。 在决定什么可能是过早优化之前,您应该衡量成本是多少。 可能有人认为,如果你不得不问这个问题,你就不知道做得对不对,你就有被烧伤的危险。
请注意,您无法创建该类型的实际变量; 要使其工作,编译器需要知道结构实际有多大,这意味着您需要从头部开始的详细信息。
严格来说,如果您不知道标签名称,那将无法正常工作。 同样,如果结构没有标签,你也不能这样做。 如果它不是结构类型,则不能这样做。
请注意,如果您知道结构标记,则还可以编写:
struct FOO *fp;
如果你必须具有创造性,那么一切都适用于传递指针,直到你达到需要访问实际库函数的程度。 然后你需要实际的库头(以确保信息是正确的),如果你的结构标签是错误的,所有地狱都会破裂。 所以,如果你打算玩这个游戏,请确保你的结构标签正确。
请注意,C11允许重复typedef,只要它每次都相同,而早期版本的C不允许这样做。 这可能是一个相当大的帮助。
工作实例
这接近一个显示如何完成的最小示例。 它假设C11,其中重复的typedef
是合法的。 它不适用于C99或C89 / C90,因为编译projfunc.c
时会重复FOO
的typedef
。 (有各种方法可以调整它以便它可以在C99或更早版本中工作,但它们更混乱,使用#ifdef
或类似于project.h
类似方法 – 因为假设你不能改变library.h
;如果可以,它毕竟是你项目的一部分。)
project.h
头文件主要由属于使用定义FOO
的库的项目的通用代码使用 – 在本例中由projmain.c
表示。 它可以单独使用,也可以使用library.h
,它由projfunc.c
说明,它是实际与库接口并调用库的项目代码。 文件library.c
仅使用library.h
。
您可以在project.h
中使用FOO
替代声明来查看哪里出了问题。 例如, typedef struct BAR FOO;
将失败; 那么typedef struct FOO *FOO;
。
project.h
#ifndef PROJECT_H_INCLUDED #define PROJECT_H_INCLUDED typedef struct FOO FOO; typedef struct Project { FOO *foop; char *name; int max; double ratio; } Project; extern int proj_function(Project *pj); #endif /* PROJECT_H_INCLUDED */
library.h
#ifndef LIBRARY_H_INCLUDED #define LIBRARY_H_INCLUDED typedef struct FOO { int x; int y; } FOO; extern FOO *foo_open(const char *file); extern int foo_close(FOO *foop); extern int foo_read(FOO *foop, int *x, int *y); extern int foo_write(FOO *foop, int x, int y); #endif /* LIBRARY_H_INCLUDED */
projmain.c
#include "project.h" int main(void) { Project pj = { 0, 0, 0, 0.0 }; if (proj_function(&pj) != 0) return 1; return 0; }
projfunc.c
#include "project.h" #include "library.h" #include int proj_function(Project *pj) { int x, y; pj->foop = foo_open("classic-mode"); if (foo_write(pj->foop, 1, 2) < 0) { foo_close(pj->foop); return -1; } if (foo_read(pj->foop, &x, &y) < 0) { foo_close(pj->foop); return -1; } printf("x = %d, y = %d\n", x, y); return 0; }
LIBRARY.C
#include "library.h" #include static FOO foo = { 0, 0 }; FOO *foo_open(const char *file) { assert(file != 0); return &foo; } int foo_close(FOO *foop) { assert(foop == &foo); foo.x = foo.y = 0; return 0; } int foo_read(FOO *foop, int *x, int *y) { assert(foop == &foo); *x = foop->x + 1; *y = foo.y + 1; return 0; } int foo_write(FOO *foop, int x, int y) { assert(foop == &foo); foo.x = x + 1; foop->y = y + 2; return 0; }
图书馆应该为您定义FOO,不透明或透明,因为它自己的来源是指FOO。
#include
应该为您提供库提供的函数的原型,以及与它们交互所需的类型。
如果你创建了自己的FOO类型,当你从库中包含函数原型时,你几乎肯定会得到一个编译错误,表明’FOO’的多个定义。