我假设不能 forward-declare 库的不透明指针类型是否正确?
Am I correct to assume one cannot forward-declare a library's opaque pointer type?
有很多关于前向声明和不透明类型的问题,但大多数似乎是从库作者的角度出发的,或者是人们试图使用不带指针的不完整类型等。
我正在使用其接口 accepts/returns FOO *
指针的库。我想确认我不能(或不应该)以某种方式 forward-declare FOO
或 FOO *
在我的 header 文件中(它定义了一个结构 FOO *
会员).
我知道我可以 #include <library.h>
在我的 header 和我的 .c 文件中,但由于这实际上只是一个学习项目,我想得到澄清。 (一方面,似乎 forward-declaration 可能是可能的,因为我的结构成员只是一个指针,因此它的大小在不知道 FOO
是什么的情况下是已知的——但另一方面,我不知道它是 valid/smart 到 typedef
还是 FOO
图书馆已经这样做了。)
提前致谢!
假设您永远不需要取消引用指针,那么如果您知道它的结构标记名称,则可以使用不透明类型指针:
typedef struct FOO FOO;
您现在可以创建 FOO *
变量并使用它们。您可能可以从 header 文件中找到结构标记,但您应该知道库所有者可以随时更改它。
通常最好包含官方 header,但如果您的大部分代码不访问实际的库,只是将句柄传递给库返回的内容,您可以避免 'cost' 包括实际的 header。在决定什么可能是过早优化之前,您应该衡量该成本是多少。有人可能会争辩说,如果你不得不问这个问题,那么你的知识还不足以确保做对,而且你有被烧毁的危险。
请注意,您不能创建该类型的实际变量;为此,编译器需要知道结构实际有多大,这意味着您需要 header.
中的详细信息
严格来说,如果您不知道标签名称,那是行不通的。同样,如果结构没有标签,你也不能这样做。而如果不是结构类型,就不行了。
注意,如果知道结构标签,也可以这样写:
struct FOO *fp;
如果您必须发挥创造力,一切都可以传递指针,直到您到达需要访问实际库函数的地步。然后你需要实际的库 header(以确保信息是正确的),如果你的结构标签是错误的,一切都会崩溃。所以,如果你要玩这个游戏,请确保你的结构标签是正确的。
请注意,C11 允许重复 typedef,只要它每次都相同,而早期版本的 C 不允许这样做。这将是一个很大的帮助。
工作示例
这接近于说明如何完成的最小示例。它假定 C11,其中重复的 typedef
是合法的。它不适用于 C99 或 C89/C90,因为 FOO
的 typedef
在编译 projfunc.c
时会重复。 (有多种方法可以调整它,使其在 C99 或更早版本中工作,但它们更混乱,使用 #ifdef
或围绕 project.h
typedef 的类似方法——因为假设你可以' t 改变 library.h
;如果可以的话,它毕竟是你项目的一部分。)
project.h
header 主要由属于项目的通用代码使用,该项目使用定义 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 <stdio.h>
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 <assert.h>
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 <library.h>
应该让您了解库提供的函数的原型,以及与它们交互所需的类型。
如果您创建自己的 FOO 类型,当您从库中包含函数原型时,几乎肯定会收到一个编译错误,指示 'FOO' 的多个定义。
有很多关于前向声明和不透明类型的问题,但大多数似乎是从库作者的角度出发的,或者是人们试图使用不带指针的不完整类型等。
我正在使用其接口 accepts/returns FOO *
指针的库。我想确认我不能(或不应该)以某种方式 forward-declare FOO
或 FOO *
在我的 header 文件中(它定义了一个结构 FOO *
会员).
我知道我可以 #include <library.h>
在我的 header 和我的 .c 文件中,但由于这实际上只是一个学习项目,我想得到澄清。 (一方面,似乎 forward-declaration 可能是可能的,因为我的结构成员只是一个指针,因此它的大小在不知道 FOO
是什么的情况下是已知的——但另一方面,我不知道它是 valid/smart 到 typedef
还是 FOO
图书馆已经这样做了。)
提前致谢!
假设您永远不需要取消引用指针,那么如果您知道它的结构标记名称,则可以使用不透明类型指针:
typedef struct FOO FOO;
您现在可以创建 FOO *
变量并使用它们。您可能可以从 header 文件中找到结构标记,但您应该知道库所有者可以随时更改它。
通常最好包含官方 header,但如果您的大部分代码不访问实际的库,只是将句柄传递给库返回的内容,您可以避免 'cost' 包括实际的 header。在决定什么可能是过早优化之前,您应该衡量该成本是多少。有人可能会争辩说,如果你不得不问这个问题,那么你的知识还不足以确保做对,而且你有被烧毁的危险。
请注意,您不能创建该类型的实际变量;为此,编译器需要知道结构实际有多大,这意味着您需要 header.
中的详细信息严格来说,如果您不知道标签名称,那是行不通的。同样,如果结构没有标签,你也不能这样做。而如果不是结构类型,就不行了。
注意,如果知道结构标签,也可以这样写:
struct FOO *fp;
如果您必须发挥创造力,一切都可以传递指针,直到您到达需要访问实际库函数的地步。然后你需要实际的库 header(以确保信息是正确的),如果你的结构标签是错误的,一切都会崩溃。所以,如果你要玩这个游戏,请确保你的结构标签是正确的。
请注意,C11 允许重复 typedef,只要它每次都相同,而早期版本的 C 不允许这样做。这将是一个很大的帮助。
工作示例
这接近于说明如何完成的最小示例。它假定 C11,其中重复的 typedef
是合法的。它不适用于 C99 或 C89/C90,因为 FOO
的 typedef
在编译 projfunc.c
时会重复。 (有多种方法可以调整它,使其在 C99 或更早版本中工作,但它们更混乱,使用 #ifdef
或围绕 project.h
typedef 的类似方法——因为假设你可以' t 改变 library.h
;如果可以的话,它毕竟是你项目的一部分。)
project.h
header 主要由属于项目的通用代码使用,该项目使用定义 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 <stdio.h>
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 <assert.h>
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 <library.h>
应该让您了解库提供的函数的原型,以及与它们交互所需的类型。
如果您创建自己的 FOO 类型,当您从库中包含函数原型时,几乎肯定会收到一个编译错误,指示 'FOO' 的多个定义。