我试图将项目从VS2013升级到VS2017。我有一切工作除了一件事 - 我在调用printf时遇到崩溃。崩溃如下:
Unhandled exception at 0x0073910C in exename.exe: An invalid parameter was passed to a function that considers invalid parameters fatal.
exename.exe!_invoke_watson(const wchar_t * expression, const wchar_t * function_name, const wchar_t * file_name, unsigned int line_number, unsigned int reserved) Line 224 C++
exename.exe!_invalid_parameter(const wchar_t * const expression, const wchar_t * const function_name, const wchar_t * const file_name, const unsigned int line_number, const unsigned int reserved) Line 112 C++
exename.exe!_invalid_parameter_noinfo() Line 117 C++
exename.exe!_isatty(int fh) Line 17 C++
exename.exe!__acrt_stdio_begin_temporary_buffering_nolock(_iobuf * public_stream) Line 43 C++
[Inline Frame] exename.exe!__acrt_stdio_temporary_buffering_guard::{ctor}(_iobuf * const stream) Line 399 C++
exename.exe!common_vfprintf::__l2::<lambda>() Line 36 C++
exename.exe!__crt_seh_guarded_call<int>::operator()<void <lambda>(void),int <lambda>(void) &,void <lambda>(void) >(__acrt_lock_stream_and_call::__l2::void <lambda>(void) && setup, common_vfprintf::__l2::int <lambda>(void) & action, __acrt_lock_stream_and_call::__l2::void <lambda>(void) && cleanup) Line 204 C++
exename.exe!__acrt_lock_stream_and_call<int <lambda>(void) >(_iobuf * const stream, common_vfprintf::__l2::int <lambda>(void) && action) Line 256 C++
[Inline Frame] exename.exe!common_vfprintf(const unsigned __int64 options, _iobuf * const stream, const char * const format, __crt_locale_pointers * const locale, char * const arglist) Line 34 C++
exename.exe!__stdio_common_vfprintf(unsigned __int64 options, _iobuf * stream, const char * format, __crt_locale_pointers * locale, char * arglist) Line 58 C++
[Inline Frame] exename.exe!_vfprintf_l(_iobuf * const _Stream, const char * const _Format, __crt_locale_pointers * const) Line 638 C++
exename.exe!printf(const char * const _Format, ...) Line 953 C++
这条线看起来像这样:
printf("simple string goes here");
我已经跟踪了这些线路的崩溃:
freopen("/dev/null", "a", stdout);
freopen("/dev/null", "a", stderr);
我可以通过用以下代码替换行来修复它:
freopen("nul", "a", stdout);
freopen("nul", "a", stderr);
对于那些可能比我更了解比赛内幕的人,我有三个问题:
修改
似乎没有可移植的方式来做到这一点,所以我选择了旧的平台特定代码。
const char* nullStream = "/dev/null";
#if defined(WIN32)
nullStream = "nul:";
#endif
// If we fail to redirect either of these streams, we will crash the nex
// time we try to use them. So we assert to be sure.
if (!freopen(nullStream, "a", stdout)) assert(false);
if (!freopen(nullStream, "a", stderr)) assert(false);
答案 0 :(得分:2)
这是Windows和Unix之间存在不可调和差异的地方之一,您只需处理。我会这样做:
#ifdef _WIN32
#define NULL_DEVICE "NUL:"
#else
#define NULL_DEVICE "/dev/null"
#endif
然后
freopen(NULL_DEVICE, "w", stdout);
在Windows案例中在NUL之后放置冒号可以防止他们在未来的Windows版本中决定他们不希望在每个目录中都使用魔术名称。
我不知道为什么"/dev/null"
在VS2013中工作 - 根据我所知道的一切,它不应该有。
答案 1 :(得分:1)
- 这在VS2013中有效但在VS2017中崩溃(没有操作系统更改)。那是为什么?
醇>
因为MS决定应该改变。 C或C ++中的任何内容都不要求任何替代方案应该具有您所追求的效果,因为freopen()
的第一个参数的解释是实现定义的。不同的实现可以以不同的方式定义它。
对于它的价值,/dev/null
是一种UNIX主义。 Windows通常不具有该名称的文件,并且它无论如何都不像UNIX那样在文件系统上提供设备文件。因此,如果您的代码在Windows上使用MSVC ++ 2013,则该实现必须为其提供特殊情况行为。
- 将Windows定位为将所有stdout / stderr输出重定向为空的正确方法是什么,这样printf就不会生成任何可见的内容 输出
醇>
类似于UNIX /dev/null
的Windows设备名为nul
(更常见的是NUL
- 就像常规文件名一样,大小写不重要)。我说使用这是一个不错的选择,但它仍然依赖于实现。
- 有一种安全的跨平台方式吗?我不一定在寻找插入标准的东西,而是寻找某种东西 每个主要桌面目标(windows,linux,mac)都可以使用。
醇>
这基本上是C或C ++实现的问题,而不是运行该实现的平台。但是,大多数实现都提供了与fopen()
和freopen()
一起使用的文件名,它们与shell和操作系统的其余部分具有相同的重要性。在这方面,VS2013似乎有点奇怪,但它并没有因此而失败。因此,没有,没有单个文件名对每个C和C ++实现都有意义。甚至不能保证任何带有该重要性的文件名。
实际上,您可以依赖/dev/null
用于Mac和Linux,而NUL
用于Windows。然后,您可以考虑使用条件编译来根据编译环境选择其中一个(参见How do I check OS with a preprocessor directive?)。