gpt4 book ai didi

c - fscanf() 跨不同编译器的不一致行为(消耗尾随空字符)

转载 作者:太空狗 更新时间:2023-10-29 14:54:40 27 4
gpt4 key购买 nike

我用 C99 编写了一个完整的应用程序,并在两个基于 GNU/Linux 的系统上对其进行了全面测试。当尝试在 Windows 上使用 Visual Studio 编译它导致应用程序行为异常时,我感到很惊讶。起初我无法断言哪里出了问题,但我尝试使用 VC 调试器,然后我发现了关于 stdio.h 中声明的 fscanf() 函数的差异.

下面的代码足以说明问题:

#include <stdio.h>

int main() {
unsigned num1, num2, num3;

FILE *file = fopen("file.bin", "rb");
fscanf(file, "%u", &num1);
fgetc(file); // consume and discard \0
fscanf(file, "%u", &num2);
fgetc(file); // ditto
fscanf(file, "%u", &num3);
fgetc(file); // ditto
fclose(file);

printf("%d, %d, %d\n", num1, num2, num3);

return 0;
}

假设 file.bin 恰好包含 512\0256\0128\0:

$ hexdump -C file.bin
00000000 35 31 32 00 32 35 36 00 31 32 38 00 |512.256.128.|

现在,当在 Ubuntu 机器上使用 GCC 4.8.4 编译时,生成的程序会按预期读取数字并将 512, 256, 128 打印到标准输出。
在 Windows 上使用 MinGW 4.8.1 编译它会得到相同的预期结果。

但是,当我使用 Visual Studio Community 2015 编译代码时,似乎存在重大差异;即,输出是:

512, 56, 28

如您所见,fscanf() 已使用尾随空字符,因此 fgetc() 捕获并丢弃对数据完整性至关重要的字符。

注释掉 fgetc() 行可以使代码在 VC 中运行,但在 GCC(可能还有其他编译器)中会破坏它。

这是怎么回事,我如何将其转换为可移植的 C 代码?我遇到了未定义的行为吗?请注意,我假设使用 C99 标准。

最佳答案

TL;DR:您遇到了 MSVC 不一致问题,这是一个长期存在的问题,MS 从未对解决它表现出太大兴趣。如果除了符合 C 实现之外还必须支持 MSVC,那么一种方法是在通过 MSVC 编译程序时使用条件编译指令来抑制 fgetc() 调用。


我倾向于同意通过格式化 I/O 函数读取二进制数据是一个有问题的计划的评论。然而,更值得怀疑的是,

compil[ing] it using Visual Studio on Windows

assuming the C99 standard.

据我所知,没有 版本的 MSVC 符合 C99。最新版本可能在符合 C2011 方面做得更好,部分原因是 C2011 使一些在 C99 中是强制性的功能成为可选功能。

但是,无论您使用的是哪个版本的 MSVC,我都认为它不符合这方面的标准(C99 和 C2011)。这是来自C99, section 7.19.6.2的相关文本

A conversion specification is executed in the following steps:

[...]

An input item is read from the stream [...]. An input item is defined as the longest sequence of input characters which does not exceed any specified field width and which is, or is a prefix of, a matching input sequence. The first character, if any, after the input item remains unread.

标准非常明确,第一个与输入序列不匹配的字符保持未读状态,因此可以认为 MSVC 符合标准的唯一方法是 \0 字符是否可以被解释为一部分匹配输入序列的(和终止),或者如果 fgetc() 被允许跳过 \0 字符。我认为后者没有理由,特别是考虑到流是以二进制模式打开的,所以让我们考虑前者。

对于 u 转换说明符,匹配的输入序列是 defined作为一个

Matches an optionally signed decimal integer, whose format is the same as expected for the subject sequence of the strtoul function with the value 10 for the base argument.

定义了“strtoul 函数的主题序列”in that function's specifications :

First, they decompose the input string into three parts: an initial, possibly empty, sequence of white-space characters (as specified by the isspace function), a subject sequence resembling an integer represented in some radix determined by the value of base, and a final string of one or more unrecognized characters, including the terminating null character of the input string.

请特别注意,终止空字符明确归因于无法识别的字符的最终字符串。它不是主题字符串的一部分,因此当 fscanf() 根据 u 说明符转换输入时,它不应与 fscanf() 匹配。

关于c - fscanf() 跨不同编译器的不一致行为(消耗尾随空字符),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42420784/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com