【问题标题】:Am I correct to assume one cannot forward-declare a library's opaque pointer type?我是否正确假设不能前向声明库的不透明指针类型?
【发布时间】:2015-03-10 19:59:31
【问题描述】:

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

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

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

提前致谢!

【问题讨论】:

  • 你怎么知道转发声明为什么?可能是typedef int FOO;typedef struct FOO FOO; 或其他。
  • 您已经在最后一段中回答了您的问题。你应该始终#include <library.h>,并且当图书馆已经在这样做时,你永远不应该将某些内容键入FOO
  • 很久没有 C(双关语),但是如果你在你的标题中 #include <library.h>,如果你使用在其中声明的类型,你应该在文件中使用 #include "yourfile.h" .c,就像你应该做的那样,你不需要在你的 file.c 中#include <library.h>。我总是在我的头文件和.c 中包含我需要的每个库,我只会在我的.c 中包含我自己的头文件和stdio.h 或math.h 之类的东西。但就像我一开始说的:很久没有 C,所以我可能错了。

标签: c forward-declaration opaque-pointers


【解决方案1】:

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

typedef struct FOO FOO;

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

通常最好包含官方标头,但如果您的大部分代码没有访问实际的库,只是传递库返回的东西的句柄,您可以避免包含实际的“成本”标题。在决定什么可能是过早优化之前,您应该衡量该成本是多少。有人可能会争辩说,如果你不得不问这个问题,你就没有足够的知识来确保它做对了,而且你有被烫伤的危险。

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

严格来说,如果你不知道标签名称,那是行不通的。同样,如果结构没有标签,你也不能这样做。而且如果不是结构类型,你就做不到。

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

struct FOO *fp;

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

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

工作示例

这接近于一个显示如何完成的最小示例。它假定 C11 中重复的 typedef 是合法的。它不适用于 C99 或 C89/C90,因为在编译 projfunc.c 时,FOOtypedef 会重复。 (您可以通过多种方式对其进行调整,使其在 C99 或更早版本中工作,但它们更混乱,使用 #ifdefproject.h 周围的类似类型定义 - 因为假设是您不能更改 @987654330 @; 如果可以的话,它毕竟是你项目的一部分。)

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

您可以在project.h 中使用FOO 的替代声明,看看哪里出了问题。例如,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 */

图书馆.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;
}

图书馆.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;
}

【讨论】:

  • 如果他关心声明而不是typedef,他也可以简单地使用struct FOO;
  • @MooingDuck: 是的,他可以使用struct FOO *fp;,只要struct FOO * 不首先出现在函数原型中,一切都会好起来的。但关键是要知道要使用的正确结构标签。 (有很多“它会在实践中工作”,直到您获得需要使用实际库的代码为止。)
【解决方案2】:

库应该为您定义 FOO,无论是不透明的还是透明的,因为它自己的源是指 FOO。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-12
    • 1970-01-01
    • 1970-01-01
    • 2011-06-14
    • 1970-01-01
    • 2011-02-12
    • 2010-09-18
    相关资源
    最近更新 更多