如何在C中转发声明FILE *
?我通常使用struct MyType;
执行此操作,但自然这似乎不可能。
如果C标准或编译器与C ++之间的行为不同,这也是有意义的。
为什么我要把这个放在一边:我要问的是如何转发声明非结构/“typedef'd结构”类型,以便我可以声明指向它的指针。显然使用void *
并将其强制转换为源文件有点hackish。
答案 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 ++,一个引用)到该类型并且只要不使用该符号就编译该声明(例如,在头部中向前声明一个符号,并且然后包括使用它在源中正确声明符号的标题。)
因此,前向声明包括手段:
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;