【问题标题】:how to set a FILE** variable to stdout?如何将 FILE** 变量设置为标准输出?
【发布时间】:2013-08-29 08:12:55
【问题描述】:

我的生产代码样机:

/* version 1 */
#include <stdio.h>
FILE** fout = &stdout;
int main() {
     fprintf( *fout, "hello\n" );
}

在 gcc 下工作正常,但据报道无法在 mingw 下编译(需要左值作为一元 '&' 操作数)。

我见过Is setting a FILE* equal to stdout portable?;我明白了

/* version 2 */
#include <stdio.h>
int main() {
    FILE* fout = stdout;
    fprintf( fout, "hello\n" );
}

将是完全有效的。但是,我需要预设一个全局变量。不幸的是,

/* version 3 */
#include <stdio.h>
FILE* fout = stdout;
int main() {
    fprintf( fout, "hello\n" );
}

不适合替换版本1;它甚至不能在 gcc 下编译(第 2 行:初始化元素不是常量)。

知道如何将标准输出放入在 main() 启动之前初始化的变量中吗?

【问题讨论】:

  • 标准输出流已经打开!不能同时打开 2 个标准输出...我建议可以使用#define,或者干脆让 main 启动然后将其存储到全局变量中。为什么在 main 启动之前?
  • 为什么在 main 开始之前:作为一个库的作者,我为我的用户提供了一个在库代码中初始化的 struct default_settings,它应该包含 fout=stdout 以及许多其他参数设置。跨度>

标签: c mingw


【解决方案1】:

在 Linux (glibc) 中,stdout 的定义如下:

extern struct _IO_FILE *stdout;

所以,你可以用它做任何你想做的事情。

然而,在 MinGW 上,stdout 是这样定义的,在 stdio.h

#define stdout  (&_iob[STDOUT_FILENO])

唉,这不是您可以获取地址的东西,而且,正如您所发现的,它不是您可以在全局初始化程序中使用的东西。 :-(

问题的根源在于 C 标准规定这些应该是宏,这意味着任何可移植程序都不应该对里面的内容做任何假设。所以,恐怕没有简单的方法可以避免以编程方式阅读stdout。这就是为什么许多库需要lib_initialize() 函数,该函数必须在其他任何事情之前调用。

C++ 确实允许全局变量的构造函数,并且在 main 之前自动调用这些构造函数,即使对于库也是如此。使用gcc 可以破解 C 程序来做同样的事情,但这是一个邪恶的把戏,我不记得该怎么做了。

我会这样做:

#include <stdio.h>
FILE* fout = NULL;

int my_library_function() {
    if (!fout)
      fout = stdout;
    fprintf( fout, "hello\n" );
}

这不是一个大的效率问题:无论如何你都必须加载fout,并且与零进行比较非常便宜。

【讨论】:

    【解决方案2】:

    根据C standard (7.21.1),stdout 是一个宏,它是“指向文件的指针”类型的表达式。它不一定是全局变量。它不是可移植的 C 来获取它的地址 --- 它在 gcc 中工作,但在 mingw 中不工作,如您所见。

    使用您的代码的第二个版本 --- 这是可移植的。

    如果您将fout初始化移到main 内,第三个也可以:

    /* version 3 */
    #include <stdio.h>
    FILE* fout;
    int main() {
        fout = stdout;
        fprintf( fout, "hello\n" );
    }
    

    此初始化不能与fout 的声明结合使用,因为stdout 不是(至少,不一定)一个常量表达式。

    如果你想要一个FILE ** 指针,使用:

    /* version 4 */
    #include <stdio.h>
    FILE* mystdout;
    FILE** fout = &mystdout;
    int main() {
         mystdout = stdout;
         fprintf( *fout, "hello\n" );
    }
    

    但同样的原因,mystdout 的初始化不能在其声明处。

    【讨论】:

    • 感谢您的链接和解释。但是“使用任何其他两个版本的代码——这些都是可移植的”——我无法确认;正如我所写,我的版本 3 无法编译。我不在乎 fout 的类型是 FILE* 还是 FILE**。对我来说重要的是我的图书馆的用户应该默认得到fout=stdout;不应要求他们在代码中插入类似fout=stdout 的行;这应该在 main() 启动之前默认初始化。
    • 对,版本 3 要求在 main 内初始化全局变量,正如我在回答中所写的那样。我正在编辑。
    • @JoachimWuttke 那你为什么不简单地使用一个外部可见的全局变量呢?然后用户可以方便地写globalFile = some_other_file;
    • @H2CO3 确实,我正在尝试使用全局变量;查看我的版本 1 和 3。问题是如何在我的库中初始化它,因此在 main 启动之前。
    • @JoachimWuttke,我可以看到两个选项:1)你可以有一个 initMyLibrary 函数。 2) 您可以使用NULL 对其进行初始化,并在首次使用时按需重新初始化。如果两者都不可接受,就开始一个关于库初始化的问题,这是一个更普遍的问题。这里的问题是stdout 在编译/链接时(不一定)是已知的。
    【解决方案3】:

    我认为这不是一个好主意,因为文件描述符通常不可移植。此外,它使库函数的实现非常低级,否则您将很难同步文件描述符和FILE 指针。

    但是,正如@JoachimWuttke 明确询问的那样,这是我在之前评论中的想法:

    /* version 5 */
    #include <stdio.h>
    #include <unistd.h>
    
    int fdout = 1;
    
    void use(FILE *fout)
    {
      fdout = fileno(fout);
    }
    
    void printing() {
      const char msg[] = "hello\n";
      write(fdout, msg, sizeof(msg)-1);
    }
    
    int main() {
      printing(); // this one goes to stdout
      FILE *f = fopen("output.txt", "wt");
      use(f);
      printing(); // this one goes to "output.txt"
      printing(); // this one too
      fclose(f);
      use(stdout);
      printing(); // this one goes to stdout too
      return 0;
    }
    

    【讨论】:

    • 谢谢你,@nickie。第一个使初始化像我想象的那样简单的答案 - 但是以fprintf 替换为write 为代价。我怕我不愿意付出这个代价。因此,我认为没有简单而干净的解决方案。
    • FILE* fp_ = fdopen(fdout, "w"); -- 在这种情况下并不优雅
    猜你喜欢
    • 2020-12-16
    • 2023-04-01
    • 2017-04-20
    • 2012-06-25
    • 1970-01-01
    • 1970-01-01
    • 2014-05-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多