转发申报文件*

时间:2010-10-07 13:55:25

标签: c++ c gcc forward-declaration c89

如何在C中转发声明FILE *?我通常使用struct MyType;执行此操作,但自然这似乎不可能。

如果C标准或编译器与C ++之间的行为不同,这也是有意义的。

Update0

为什么我要把这个放在一边:我要问的是如何转发声明非结构/“typedef'd结构”类型,以便我可以声明指向它的指针。显然使用void *并将其强制转换为源文件有点hackish。

6 个答案:

答案 0 :(得分:25)

你做不到。该标准只是声明FILE是“能够记录控制流所需的所有信息的对象类型”;这取决于实现这是typedef struct(你的名字你还不知道),或其他什么。

声明FILE的唯一可移植方式是使用#include <stdio.h>(或C ++中的<cstdio>)。

答案 1 :(得分:11)

如果你#include <stdio.h>,你应该获得FILE typedef。这是唯一真正安全且可移植的方式 - 你没有没有类型别名的typedef,并且不能保证什么类型FILE别名,所以每个编译器或libc或其他什么都可以有不同的别名。但是如果有人真正想要#include <stdio.h>,你需要这种类型是正确的,以免不一致的定义导致错误。

编辑:

现在我想起来,可能还有另一种我能想到的方式。它不是一个typedef,它是通过劫持“FILE”定义而起作用的邪恶宏观内容。我不会仅仅因为这个原因而推荐它。但它可能适合您的需要。

#ifdef USES_REAL_FILE_TYPE
#include <stdio.h>
#else
#define FILE void
#endif

/* declare your struct here */

#ifndef USES_REAL_FILE_TYPE
#undef FILE
#endif

然后#define USES_REAL_FILE_TYPE将文件包含在需要真实FILE *的任何代码中之前,其余代码只会将指针视为void *

我不保证这不会弄乱东西。特别是,在任何你想知道关于这种假类型的真实内容的情况下它会破坏,并且所有触及指针的代码都可能需要#define。但如果你已经死定了“不必要的”#includes,那么你唯一的方法是在不干扰stdio的情况下获得FILE *。你无法转发声明一个typedef。

EDIT2:

好的,我检查过以确保。不确定它是多么标准,或者你可以用它做什么,但是......

typedef FILE;

在Visual C和GCC中都有效,但在编译C代码时只能 。似乎C ++标准明确地说某个地方没有类型就没有typedef。但是,C语言并没有。

但是,它似乎不允许向前声明类型,而不是在GCC中。如果您在之后尝试typedef int FILE;,则会引发有关冲突的typedef的错误。然而,VS似乎允许它,只要它是一个整数类型。似乎typedef X在VS中确实意味着typedef int X(显然,在C99中)。无论哪种方式,GCC都不会让你重做typedef,即使是完全相同的类型。

答案 2 :(得分:6)

FILE是一个围绕结构的typedef,你不应该过多探讨(比如你不应该使用WinAPI句柄背后的数据),除非通过它的专用API函数。

前向声明?

正向声明允许一个人声明一个指针(或C ++,一个引用)到该类型并且只要不使用该符号就编译该声明(例如,在头部中向前声明一个符号,并且然后包括使用它在源中正确声明符号的标题。)

因此,前向声明包括手段:

  • 更快的编译
  • less coupling

Chuck Typedef vs. Forward-declaring?

typedef的问题在于它们很难处理,因为正如您所发现的那样,您无法向前声明它们。

因此,您无法转发声明FILE,也无法转发声明std::string。所以你别无选择,只能包含标题来处理它们。

(这就是我讨厌C入侵C ++代码中的typedef struct { /* ... */ } MyTypedefedType ;模式的原因:它在C ++中没用,它阻止了前向声明。)

前向声明标准符号?

好的方面是,如果符号是“标准”,那么包含它们的标题应该不会太痛苦。耦合并没有那么多问题,如果它会稍微减慢编译速度,即使通过使用预编译头文件也可以轻松实现。

<iosfwd>:有些人想到你了!

C ++标准库提供<iosfwd>标题。

如果您需要的只是前向声明,则可以包含<iosfwd>,而不是包含任何(或所有)C ++流标题。

答案 3 :(得分:4)

FILE是系统相关的typedef。您不应该关心如何定义或甚至命名实际结构。但是你总是可以查看你的/usr/include/stdio.h文件:)

答案 4 :(得分:0)

正如已经指出的那样,没有可移植的方式来转发声明FILE结构或类型定义。

但是,可以更改自己设施的接口以依赖普通整数,然后使用 fileno 功能(也可通过#include <stdlib.h>获得)

详细步骤
0.找到您当前的界面。例如:
void myprint(FILE* stream, ...);
1.使用整数文件描述符(fd)而不是FILE*
   void myprint(int stream_fd, ...);
2.使用fileno而不是FILE*来呼叫新界面:
   myprint(fileno(stream));

但缺点是您的实现(上例中的myprint)需要使用文件描述符而不是FILE*来重写实际的I / O例程。重写实现的另一种方法是使用给定的描述符简单地fdopen FILE

void myprint(int stream_fd, ...)
{
  FILE *const stream = fdopen(stream_fd);
  /* your existing implementation follows */
  fclose(stream);
}

以上反过来会让您考虑“拥有”资源的位置,以便在不再需要时关闭FILE。开/关序列通常很好(如上所示),但在更复杂的情况下,需要通过使用追加模式等打开文件来调整实现(我们试图避免)。

答案 5 :(得分:-3)

FILE *是一种不透明的类型。因此,这应该在理论上有效。

typedef struct FILE_impl_but_including_stdio_h_is_best FILE;