我正在尝试使用 stdio.h 中的 c 库函数 fputc
我假设它应该根据https://linux.die.net/man/3/fputc上的规范工作
具体来说,感兴趣的部分是:
根据这些信息,我假设如果 fputc 成功将字符写入提供的流,我应该收到一个等于写入字符的返回值,如果写入流失败,我应该得到 EOF 的值。
这是我的代码:
// COMPILE
// gcc -Wall -Wextra -Werror -O3 -s ./fputc.c -o ./fputc-test
// RUN
// ./fputc-test
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
void print_error_exit();
int main() {
FILE * fp;
// ensure file exists
if((fp = fopen("file", "w")) == NULL) print_error_exit();
// close stream
if(fclose(fp) == EOF) print_error_exit();
// open file in read-only mode
if((fp = fopen("file", "r")) == NULL) print_error_exit();
// close stream
if(fclose(fp) == EOF) print_error_exit();
printf("EOF is: %d\n", EOF);
// use fputc on a read-only closed stream (should return error, right?)
printf("fputc returned: %d\n", fputc(97, fp)); // 97 is ascii for 'a'
// expected:
// prints EOF (probably -1)
// actual:
// prints 97 on linux with both gcc and clang (multiple versions tested)
// prints -1 on windows with mingw-gcc
return EXIT_SUCCESS;
}
void print_error_exit() {
fprintf(stderr, "%s\n", strerror(errno));
exit(EXIT_FAILURE);
}
Run Code Online (Sandbox Code Playgroud)
我已经使用 gcc 8.1.0、gcc 8.3.0、gcc 9.3.0 和 clang 7.0.1 在 Ubuntu 20、Debian 9、Windows 10 上测试了代码。在 Windows 上,我使用了 mingw。
我发现的唯一趋势是 fputc 在 Windows 上返回我期望的结果,而不是在 linux 上返回我期望的结果。
以下哪一项是正确的?
请帮助我理解这一点。当我尝试在关闭的流上使用 fputc 时,为什么 fputc 不返回错误代码 (EOF),更不用说打开仅用于读取的流了?
C17 7.21.3(4):
关联文件关闭后,指向 FILE 对象的指针的值是不确定的。
所以在你的fputc调用中,fp有一个不确定的值。该标准只定义了fputcwhenfp指向输出流的行为,我们不能说是这种情况,所以行为是未定义的。
文本有点误导,因为在典型系统上,指针的值在传递给 时不会改变fclose;毕竟,C 按值传递参数,所以即使它想FILE *fp; ...; fclose(fp);改变你的fp变量的值也不能。它仍然像往常一样指向相同的地址。但是位于该地址的数据肯定会变得不确定,并且不再需要系统将其解释为流。
以下是 Linux 幕后发生的事情。不用说,这些都是实现细节,您不应在任何程序中依赖它们。
你可以在这里看到 Linux 的fputc实际作用。该fp参数指向一个FILE对象,该对象包含一个指向缓冲区的指针和指示剩余空间的数字。如果缓冲区中有空间,则在那里写入字符;这没有办法失败。只有当缓冲区已满并且需要通过操作系统写出数据时,才有可能fputc返回错误。
现在,当您fclose处理文件时,缓冲区和FILE对象会被简单地释放,使用free()或 类似的机制。如果在此期间没有其他人分配任何内存,则这些对象的内容可能仍在内存中并且在您调用fputc. 在这样做之前,这些对象不会以任何方式被标记为无效,因为除非他们正在访问已释放的内存,否则没有人会看到该标志,并且任何正确的程序都不应该这样做。因此,当您调用 时fputc,指向的内存内容fp仍然看起来与有效FILE对象完全一样,但缓冲区未满,因此fp在那里写入字符并返回成功 - 因为毕竟,将字符写入缓冲区不会失败。但实际上你现在已经写入释放的内存,可能会导致各种麻烦。
所以它类似于 malloc 的“释放后使用”错误:系统相信你在释放资源后不会使用它,但如果你这样做了,它也不承诺会抓住你。
您测试的其他系统很可能FILE在解除分配之前设置的对象中具有某种“无效”标志,并且他们fputc可能会测试此标志。所以如果FILE对象没有被其他东西覆盖,标志的值可能仍然存在于内存中,因此fputc失败。但它仍然是错误的,因为它必须读取释放的内存才能看到标志。如果您在这两者之间做更多的工作,包括分配更多内存并写入其中,您可能还会在这些系统上看到更多不可预测的错误行为,可能包括虚假的成功回报。
| 归档时间: |
|
| 查看次数: |
89 次 |
| 最近记录: |