转发申报文件*
如何在C中转发声明FILE *
? 我通常使用struct MyType;
执行此操作struct MyType;
,但自然这似乎不可能。
如果C标准或编译器与C ++之间的行为不同,这也是有意义的。
Update0
为什么我要把这个放在一边:我要问的是如何转发声明一个非结构/“typedef’d结构”类型,以便我可以声明指向它的指针。 显然使用void *
并将其转换为源文件有点hackish。
你不能。 该标准只是声明FILE
是“能够记录控制流所需的所有信息的对象类型”; 这取决于实现是否是struct
的typedef
(无论如何你不知道它的名字),或其他东西。
声明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;