下面的C程序创建一个指定长度的空文件.我已经在我本地的Ubuntu20.04笔记本电脑上测试了这一点,它可以工作.我现在在Digital Ocean(Ubuntu 22.04)的一个实例中运行它,但是行"flose(Outfile);"抛出了一个分段错误.在云中,我在Docker容器中运行它,但不是在本地.

#include <stdio.h>
#include <string.h>
#include <errno.h>

size_t create_empty_file (char * out_fname, size_t file_size)
{
    FILE *outfile;
    char buf[1024];

    outfile = fopen(out_fname, "wb+");

    if (outfile == NULL) {
        strcpy(buf, strerror(errno));
        printf("%d --> %s\n", errno, buf); }

    fseek(outfile, file_size, SEEK_SET);

    if (errno != 0) {
        strcpy(buf, strerror(errno));
        printf("%d --> %s\n", errno, buf); }

    fputc('\0', outfile);

    if (errno != 0) {
        strcpy(buf, strerror(errno));
        printf("%d --> %s\n", errno, buf); }

    fclose( outfile );

    return 0;
}

文件确实创建了,并且文件确实关闭了--我知道这一点,因为在文件关闭之前,它显示的文件大小为零,并且文件确实显示了其指定的4 GB长度.

通常情况下,当文件指针为空时,flose会抛出段错误,但这里的情况并非如此.而且它在我本地的笔记本电脑上也能用.

以下是来自gdb的段故障详细信息:

程序接收信号SIGSEGV,分段故障. __GI__IO_setb(f=f@Entry=0x145b210,b=b@Entry=0x0,Eb=EB@Entry=0x0,a=a@Entry=0)中的0x00007fe121321c70位于/libio/genops.c:338 338./libio/genops.c:没有这样的文件或目录.

谢谢你在这方面的帮助.

推荐答案

(正如其他人指出的那样,如果可用,您应该使用ftruncate(2),特别是因为如果file_size - 1大于0x7fffffff,则fSeek(3)将不能正常工作,这在64位程序中很容易发生.我的回答解决了代码的其他问题,它假定file_size足够小.)

您没有正确处理错误,这可能会导致内存崩溃,这会导致内存崩溃.最有可能的错误是在您的程序中,而不是在fclose(...)中.

以下是如何正确处理错误:

#include <stdio.h>
#include <string.h>
#include <errno.h>

/* Returns 0 on success, -1 on error. Sets errno to a useless value. */
int create_empty_file(char *out_fname, size_t file_size) {
    FILE *outfile = fopen(out_fname, "wb");
    if (outfile == NULL) {
        fprintf(stderr, "fopen: %d: %s\n", errno, strerror(errno));
        return -1;
    }
    if (file_size != 0) {
        if (fseek(outfile, file_size - 1, SEEK_SET) != 0) {
            fprintf(stderr, "fopen: %d: %s\n", errno, strerror(errno));
            fclose(outfile);
            return -1;
        }
        fputc('\0', outfile);  /* Error checked below. */
        if (ferror(outfile) || fflush(outfile) != 0) {
            fprintf(stderr, "fputc\n");
            fclose(outfile);
            return -1;
        }
    }
    fclose(outfile);
    return 0;
}

将您的函数更改为此函数后,您将不会出现分段错误.不过,您可能会收到I/O错误(由上面的fprintf(stderr, ...)打印),请注意.也许I/O错误的原因是您的文件系统已满.(然而,这在本例中并不常见,通过查找创建一个巨大的文件应该不会占用太多的文件系统空间,大多数文件系统都会优化这些漏洞.)

以下是错误判断的一些有用的经验法则:

  • 通常,判断您调用的每个函数的返回值.

  • 仅当上一个I/O函数的返回值不成功时才使用errno.

  • 对于stdio缓冲输出(例如fputc(...)),您可以跳过错误判断,并在显式刷新后判断错误(上面代码中的if (ferror(outfile) || fflush(outfile) != 0)).

  • 一般来说,在第一个错误发生后停止文件上的I/O(并关闭它).

  • 在函数的返回值中传播错误指示.

  • 将错误打印到stderr.

我已经修复了上面的另外两个问题:

  • fopen(..., "wb+")中不需要+,因为您不想从文件中读取.
  • 你需要file_size - 1来查找,因为fputc多加了1个字节.

C++相关问答推荐

与unions 的未定义行为

VS代码C/C++扩展intellisense无法检测环境特定函数'

exit(EXIT_FAILURE):Tcl C API类似功能

由Go调用E.C.引起的内存快速增长

将指针作为参数传递给函数

带有sigLongjMP中断I/O的异常处理程序

具有交换链获取和命令缓冲区提交的同步-危险-读后写错误

在循环中复制与删除相同条件代码的性能

防止规范模式在C++中 echo 特殊字符

在另一个函数中使用realloc和指针指向指针

安全倒计时循环

无法识别C编程语言的语法,如书中所示

为什么Linux无法映射这个PT_LOAD ELF段?

程序如何解释变量中的值

根据输入/输出将 C 编译过程分为预处理、编译、汇编和链接步骤

使用复合文字数组初始化的指针数组

Makefile - 将 .o 文件放入子文件夹中

多行表达式:C 编译器如何处理换行符?

我该如何处理这个 C 90 代码中的内存泄漏?

如何根据当前舍入方向将float转换为int?