转发申报文件*

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

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

Update0

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

你不能。 该标准只是声明FILE是“能够记录控制流所需的所有信息的对象类型”; 这取决于实现是否是structtypedef (无论如何你不知道它的名字),或其他东西。

声明FILE的唯一可移植方式是使用#include (或C ++中的 )。

如果你#include 你应该得到它的FILE typedef。 这是唯一真正安全且可移植的方式 – 没有类型别名的typedef,并且无法保证FILE别名的类型,因此每个编译器或libc或其他任何东西都可以有不同的别名。 但是如果有人真正想要#include ,你需要这种类型是正确的,以免不一致的定义导致错误。

编辑:

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

 #ifdef USES_REAL_FILE_TYPE #include  #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,即使是完全相同的类型。

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

前瞻性声明?

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

因此,前向声明包括:

  • 更快的编译
  • 耦合较少

Chuck Typedef vs. Forward-declaring?

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

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

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

前向标识符号?

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

:有人在想你!

C ++标准库提供标头。

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

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

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

但是,可以将自身工具的接口更改为依赖于普通整数,然后使用fileno函数(也可通过#include )。

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

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

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

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

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

 typedef struct FILE_impl_but_including_stdio_h_is_best FILE;