MinGW 的 msvcrt 替代方案?(例如,获取符合标准的 snprintf)

4

下面是一个有趣的问题...

我们有一些 C 库,虽然在 Linux 上开发,但应该是跨平台的,因为它们只依赖于 ISO/IEC 9899:1999 中定义的 C 标准库。当我们使用 MinGW 编译这些库时,一切似乎都很顺利,但今天我们发现 msvcrt 的 snprintf() 实现与 C99 标准中的定义不兼容。

我原本希望 MinGW 警告我 -std=c99 实际上并没有完全支持。否则,我怎么知道呢?

有没有其他的 C 标准库可以用于 Windows,最重要的是:MinGW 是否可以被告知链接它们而不是 msvcrt?

如果没有,我们至少需要一个列表或其他东西,以便检查关于 msvcrt 和 c99 的其他潜在可移植性问题。

PS:我知道 Cygwin 和 MSYS2,但我更喜欢本地的 Windows 二进制文件(部分原因是我们还在 Matlab 中使用我们的库)。

编辑: 抱歉,我应该更清楚地解释一下我的问题:msvcrt 的 snprintf() 应该输出 '\0' 作为最后一个字符,如果输出不适合的话。然而,msvcrt 实际上没有这样做,因此生成的字符串不再正确终止。我不知道为什么有人会选择以这种方式实现 snprintf(),因为对我来说,省略 '\0' 没有任何意义。

我们也尝试了建议的 __USE_MINGW_ANSI_STDIO,但我想那只是修复了缺少的格式说明符?至少对于我们具体的问题似乎没有任何影响。


2
你尝试过使用__USE_MINGW_ANSI_STDIO吗? - user694733
@lundin:我重新打开了,因为它不再与重复内容匹配。 - Antti Haapala -- Слава Україні
“因为对我来说,仅仅省略掉 '\0' 完全没有任何意义。” 更可怕的是,这种做法是 百分之百正确 的,而且这就是它能够正常工作的原因。 - Antti Haapala -- Слава Україні
@AnttiHaapala 这仍然是一个重复的问题。好吧,我会在这里发布一个答案... - Lundin
1
@AnttiHaapala 我认为一个C库应该符合标准(需要'\0'),如果 - 像在这种情况下一样 - 库不兼容C99,编译器不应该仅仅用Microsoft不兼容的_snprintf()版本替换我的代码中的C99 snprintf()调用(至少不是在-std=c99模式下)。 - Felix G
显示剩余2条评论
2个回答

4
标准要求snprintf执行如下操作:

如果超出n-1个输出字符,则丢弃这些字符而不是写入数组,并在实际写入数组的字符末尾写入空字符。

实际上,msvcrt中的snprintf并不是标准的snprintf,而是微软的版本。详情可以参考此处:
Is snprintf() ALWAYS null terminating? 以下代码将得到不符合规范的结果:
#include <stdio.h>

int main (void)
{
  char dst[3];
  snprintf(dst, 3, "%c%c%c", 'A', 'B', 'C');

  for(size_t i=0; i<3; i++)
  {
    printf("%.2X ", dst[i]);
  }
}

我得到了输出 41 42 43,这不符合标准。为了获取标准的C语言输出,你需要在 stdio.h 引用之前添加以下内容:

#define __USE_MINGW_ANSI_STDIO 1

现在,您将获得 41 42 00,这是合规的。

所有这些程序安全问题的根源是微软,在过去的20年中一直在其产品中使用不符合标准的C库。


1
为了重现这个问题,我故意使用了一个非常旧的Mingw版本,<gcc 5。微软最近已经尝试将其产品更新到1999年标准,因此最新的微软产品现在甚至符合(撤销的)ISO 9899:1999标准。否则,请给他们更多时间。 - Lundin
谢谢提供的信息。到目前为止,我们已经尝试了-D__USE_MINGW_ANSI_STDIO以及#define,但是由于某些原因,我们的MinGW(5.4)仍然没有使用符合标准的版本。现在我们将测试一个像你的隔离示例,以确保它能正常工作。 - Felix G
@FelixG 如果有几个文件包含 stdio.h,可能会出现一些链接顺序问题。 - Lundin
@Lundin 没有可变长度数组。 - Antti Haapala -- Слава Україні
@Lundin 现在它可以工作了,显然当我们第一次测试时还有一些旧的.o文件在里面^^。非常感谢你。 - Felix G

2
如果你需要从MinGW-w64获取C99 stdio,你可以定义__USE_MINGW_ANSI_STDIO,这样你就可以绕过msvcrt实现。最好通过编译器参数来定义它。"Original Answer"的意思是"最初的回答"。
-D__USE_MINGW_ANSI_STDIO

另外,您可以尝试使用已设置为与新的ucrt链接的MinGW-w64构建,但我不知道是否存在任何预先存在、易于使用且稳定的构建方式。

原始答案:最初的回答


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接