【问题标题】:Output of fprintf and WriteConsole happen in reverse orderfprintf 和 WriteConsole 的输出以相反的顺序发生
【发布时间】:2014-09-10 23:51:29
【问题描述】:

我在 Windows 中看到控制台 I/O 的奇怪行为。当我使用CONOUT$ 作为路径打开FILE * 时,它应该打开控制台的stdout。如果我将该指针用于fprintf,然后是WriteConsole,您会认为消息会按各自的顺序出现,但实际上它们是相反的。

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <io.h>

int main(int argc, char *argv[]) {
  FILE *fout = fopen("CONOUT$", "w");
  fprintf(fout, "Hello world!\n");

  LPSTR goodbye = "Goodbye, cruel world!\n";
  DWORD length = strlen(goodbye);
  DWORD written;
  WriteConsole(_get_osfhandle(_fileno(fout)), goodbye, length, &written, NULL);

  return 0;
}

还有输出:

Goodbye, cruel world!
Hello world!

这是为什么?我的猜测与 Win32 I/O 函数和 stdio 同步(或者更确切地说,不同步)的方式有关。我知道 C++ iostream 需要特别注意与 stdio 同步,所以也许 Win32 不这样做?

【问题讨论】:

  • 我刚刚意识到的一个好处是:如果您将fprintf 更改为只调用printf,则排序将按您的预期工作。这是为什么呢?
  • 我想知道这是否是stdio.h 的缓冲问题,但我不确定为什么新的FILE 句柄和stdout 会有所不同。我得再读一遍标准。
  • @andlabs:“打开时,当且仅当可以确定不引用交互式设备时,流才被完全缓冲。流的错误和文件结束指示符被清除。”这就是标准所说的。但是Windows stdio库是否符合,我不知道。
  • 通过打开 CONOUT$,您绕过了对标准输出流执行的正常检查,是否需要自动刷新。

标签: c++ c winapi stdio


【解决方案1】:

这可能与 stdio.h 添加到输出的一些缓冲有关。尝试添加一个

fflush(fout);

fprintf 之后。或者你可以尝试一个

setbuf(fout, null);

为您的输出流禁用缓冲。

关于“奖励”(printf 正常工作):Afaik stout 通常设置为在每个换行符后自动刷新。

【讨论】:

    【解决方案2】:

    它几乎肯定与stdio 缓冲有关,尽管理论上fout 不应该被完全缓冲。

    C11 §7.21.5.3/8:“打开时,当且仅当可以确定不引用交互式设备时,流才被完全缓冲。流的错误和文件结束指示符被清除。”因此,Windows stdio 实现很可能无法确定CONOUT$ 是交互式设备,但标准的措辞似乎是如果有疑问,则不应以完全缓冲的方式打开流。它可能以行缓冲方式打开,但您在fprintf 中输出\n,因此在这种情况下您应该没问题,事实上,使用printf 有效这一事实或多或少地证明了这一点。

    您可以尝试使用setvbuf 关闭fout 上的缓冲,看看是否有帮助。

    【讨论】:

      【解决方案3】:

      这绝对是 stdio 的缓冲阻碍了。我收到了与 MSVC2012 和 mingw-w64 实现相同的输出。

      我决定从stdio层切换到POSIX层,输出是:

      Hello world!
      Goodbye, cruel world!
      

      您的代码,稍作修改:

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      #include <windows.h>
      #include <io.h> 
      #include <fcntl.h>
      
      int main(int argc, char *argv[]) {
        int fout = _open("CONOUT$", _O_WRONLY);
        char *hello = "Hello world!\n";
        _write(fout, hello, strlen (hello));
      
        LPSTR goodbye = "Goodbye, cruel world!\n";
        DWORD length = strlen(goodbye);
        DWORD written;
        WriteConsole(_get_osfhandle(fout), goodbye, length, &written, NULL);
      
        _close(fout);
        return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-06-24
        • 2023-03-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多