我是否正确假设无法正向声明库的不透明指针类型?

时间:2015-01-12 23:13:38

标签: c forward-declaration opaque-pointers

关于前向声明和不透明类型存在很多问题,但大多数似乎是从图书馆作者的角度出发,或者是人们试图使用不带指针的不完整类型或者其他类似的。

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

我知道我的标题和.c文件中只能#include <library.h>,但由于这只是一个学习项目,我想澄清一下。 (一方面,它似乎可能是一个前向声明,因为我的结构成员只是一个指针,因此它的大小是已知的而不知道FOO是什么 - 但另一方面,我不知道知道当库已经这样做时typedefFOO的某些内容是否有效/聪明。)

提前致谢!

2 个答案:

答案 0 :(得分:4)

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

typedef struct FOO FOO;

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

通常最好包含官方标题,但如果您的大部分代码都没有访问实际的库,只需将句柄传递给库返回的内容,就可以避免包含实际内容的“成本”头。在决定可能过早优化之前,您应该衡量成本是多少。可能有人认为,如果你不得不问这个问题,你就不知道做得对不对,而且你有被烧伤的危险。

请注意,您无法创建该类型的实际变量;为了实现这一点,编译器需要知道结构实际有多大,这意味着你需要标题中的细节。

严格地说,如果您不知道标签名称,那将无效。同样,如果结构没有标签,你也不能这样做。如果它不是结构类型,则不能这样做。

请注意,如果您知道结构标记,则还可以编写:

struct FOO *fp;

如果你必须具有创造性,那么一切都可以用来传递指针,直到你到达需要访问实际库函数的程度。然后你需要实际的库头(以确保信息是正确的),如果你的结构标签是错误的,所有地狱都会破裂。所以,如果你打算玩这个游戏,请确保你的结构标签正确。

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

工作示例

这接近一个显示如何完成的最小例子。它假定C11,其中重复的typedef是合法的。它不适用于C99或C89 / C90,因为编译typedef时会重复FOO的{​​{1}}。 (有各种方法可以调整它,以便它可以在C99或更早版本中工作,但它们更加混乱,使用projfunc.c类似于#ifdef类型的project.h或类似的 - 因为推测是你可以'改变library.h;如果可以,它毕竟是你项目的一部分。)

project.h标头主要由属于使用定义FOO的库的项目的通用代码使用 - 在此示例中由projmain.c表示。它可以单独使用,也可以library.h使用,projfunc.clibrary.c说明,它是实际与库接口并调用库的项目代码。文件library.h仅使用FOO

您可以在project.h中使用typedef struct BAR FOO;的替代声明来查看其中出现的问题。例如,typedef struct FOO *FOO;将失败; #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 */ 也是如此。

project.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 */

library.h

#include "project.h"

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

    if (proj_function(&pj) != 0)
        return 1;
    return 0;
}

projmain.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;
}

projfunc.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;
}

LIBRARY.C

{{1}}

答案 1 :(得分:1)

图书馆应该为你定义FOO,不透明或透明,因为它自己的来源是指FOO。

#include <library.h>应该为您提供库提供的函数的原型,以及与它们交互所需的类型。

如果您创建自己的FOO类型,当您从库中包含函数原型时,几乎可以肯定会收到编译错误,指示“FOO”的多个定义。

相关问题