我是否正确地假设不能向前声明库的不透明指针类型?

J C*_*per 1 c forward-declaration opaque-pointers

有很多关于前向声明和不透明类型的问题,但大多数似乎是从库作者的角度来看的,或者人们试图使用没有指针的不完整类型等。

我正在使用一个接口接受/返回FOO *指针的库。我想确认我不能(或不应该)以某种方式向前声明FOOFOO *在我的头文件中(它定义了一个带有FOO *成员的结构)。

我知道我可以只#include <library.h>在我的头文件和我的 .c 文件中,但由于这实际上只是一个学习项目,我想得到澄清。(一方面,前向声明似乎是可能的,因为我的结构成员只是一个指针,因此它的大小在不知道是什么的情况下是已知的FOO——但另一方面,我不知道它是否有效/聪明的typedef东西,FOO当库已经这样做。)

提前致谢!

Jon*_*ler 5

假设您永远不需要取消引用指针,那么如果您知道它的结构标记名称,则可以使用不透明类型指针:

typedef struct FOO FOO;
Run Code Online (Sandbox Code Playgroud)

您现在可以创建FOO *变量并使用它们。您可能可以从头文件中找到结构标记,但您应该知道库所有者可以随时更改它。

通常最好包含官方头文件,但如果您的大部分代码没有访问实际的库,只需将句柄传递给库返回的内容,您就可以避免包含实际头文件的“成本”。在决定什么可能是过早优化之前,您应该衡量成本是多少。有人可能会争辩说,如果您必须问这个问题,那么您的知识不足以确保正确地做,并且您有被烫伤的危险。

请注意,您不能创建该类型的实际变量;为此,编译器需要知道结构实际有多大,这意味着您需要头文件中的详细信息。

严格来说,如果您不知道标签名称,那将不起作用。同样,如果结构没有标签,你也不能这样做。如果它不是结构类型,你就做不到。

注意,如果你知道结构标签,你也可以这样写:

struct FOO *fp;
Run Code Online (Sandbox Code Playgroud)

如果您必须发挥创造力,那么在您到达需要访问实际库函数的点之前,一切都可以用于传递指针。然后你需要实际的库头(以确保信息正确),如果你的结构标签是错误的,一切都会崩溃。所以,如果你要玩这个游戏,请确保你得到正确的结构标签。

请注意,C11 允许重复 typedef,只要它每次都相同,而早期版本的 C 不允许这样做。这可以是一个相当大的帮助。

工作示例

这接近于展示如何完成的最小示例。它假设 C11 重复typedef是合法的。它不适用于 C99 或 C89/C90,因为typedefforFOOprojfunc.c编译时会重复。(您可以通过多种方式对其进行调整,使其能够在 C99 或更早版本中运行,但是它们在使用typedef#ifdef或类似project.htypedef 时更加混乱— 因为假设您无法更改library.h;如果可以,它是一部分毕竟是你的项目。)

project.h报头主要由属于使用定义库项目的一般代码中使用FOO,其由下式表示-projmain.c在这个例子中。它可以单独使用,也可以与 一起使用library.h,其中说明的projfunc.c是实际与库接口并调用库的项目代码。该文件library.c仅使用library.h.

您可以使用FOOin 的替代声明project.h来查看哪里出了问题。例如,typedef struct BAR FOO;会失败;也会typedef struct FOO *FOO;

项目.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 */
Run Code Online (Sandbox Code Playgroud)

图书馆.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 */
Run Code Online (Sandbox Code Playgroud)

项目主文件

#include "project.h"

int main(void)
{
    Project pj = { 0, 0, 0, 0.0 };

    if (proj_function(&pj) != 0)
        return 1;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

项目函数

#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;
}
Run Code Online (Sandbox Code Playgroud)

图书馆.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;
}
Run Code Online (Sandbox Code Playgroud)