#include <iostream>
#include <cstdio>
using namespace std;
int main() {
auto file = freopen("./input.txt", "r", stdin);
if (file == nullptr) {
cout << "freopen failed" << '\n';
} else {
int n;
cin >> n;
cout << n << endl;
}
return 0;
}
这个程序读取一个文件中的整数并输出它。它可以在其他shell上运行,如cmd,powershell和Msys 2 bash。但是在git bash上,无论输入文件是什么,它总是输出0。当freopen
用于stdout时,它也会失败。
我用MSYS 2下载的g++.exe (Rev10, Built by MSYS2 project) 12.2.0
编译。
实际上,我找到了解决这个问题的方法,我把libstdc++-6.dll
复制到MSYS的mingw 64(确切地说是msys 2/ucrt 64/bin),然后把libstdc++-6.dll
重写到git的mingw 64,它就开始工作了,看起来好像当程序在git bash上执行时,它先链接了git的mingw 64的dll,导致freopen
无法工作。
然而,我仍然想知道为什么会发生这种情况摆在首位,以及我的解决方案是否可能在未来造成问题。
- git-for-windows的libstdc++是否有bug?如果没有,问题的原因是什么?git bash是否只用于使用git,而不是用于运行可执行文件之类的?
1.我的解决方案是否有可能导致另一个问题?如果有,是否有其他更好的解决方案?
1条答案
按热度按时间a7qyws3x1#
问题是
stdin
和cin
是底层操作系统例程的缓冲I/O Package 器,并且彼此独立(这也是为什么不应该将它们混合用于输入,因为它们可能缓冲读取的一部分)。freopen()
将替换stdin
Package 器,但底层操作系统文件描述符和/或句柄不一定会被替换(在您的系统上也不会),因此cin
仍将连接到底层句柄。据我所知,在C++中没有可移植的方法来更改
cin
。但是,如果您只想支持最常用的操作系统,则有以下几种可能性:
**POSIX:**在基于POSIX的操作系统上,如Linux、macOS、FreeBSD等,您可以使用底层的
open()
函数(#include <fcntl.h>
)打开文件,然后使用dup2()
替换文件描述符0
,它是标准的输入文件描述符。(Maven技术说明:如果您的操作系统支持
O_CLOEXEC
,您可能还希望将open()
传递给open()
的标志,但请确保dup2()
也清除您的操作系统上的该标志,它在Linux上会清除该标志,因为标准描述符不应设置该标志。由于不提供该标志,此实现存在微小的争用情况,但由于我们在调用dup2()
后立即关闭fd
,我个人对此没有意见,因此在多线程环境中通常不会替换标准输入描述符。)**Windows:**在Windows上要复杂一些。你可以使用
SetStdHandle()
Win32 API函数来替换输入文件句柄本身,但这对C运行时没有帮助(在某些情况下,C运行时也是C++运行时的基础),因为它有一个基础句柄的副本。所以你还需要使用_open_osfhandle()
从句柄中获取文件描述符,并使用_dup2()
复制它(类似于POSIX版本,但这里的文件描述符只针对C运行时本身)。下面的代码应该可以在Windows上运行:
cin
/stdin
的所有代码相同时,此操作才有效。例如,如果您使用msvcrt.dll
作为项目的C运行时,但您使用的函数来自使用msvcr120.dll
本身的DLL,则该DLL不会看到更改,因为它使用的不同的C运行时会跟踪不同的内部文件描述符。如果您使用相同的编译器编译此代码和从cin
读取数据的代码,这应该不是问题,但是如果您运行的是第三方代码,而您没有使用编译器显式地重新编译这些代码,那么我不知道有什么简单的方法可以绕过这种限制。免责声明:我没有明确测试过,我有类似的代码来替换
stderr
,我在这里做了修改。我可能在翻译它的时候犯了一些错误。