存储在二进制文件中的int不成功fread(),分段错误[关闭]

问题描述 投票:0回答:1

似乎有10个问题的顺序和(大多数)成功的答案解决因C中误用的fread()引起的分段错误。尽管如此,我遇到了这样的问题,但还没有找到解决方案。

我有一个包含int(称为nbins)和floats(大小为nbins)数组的二进制文件。当我尝试读取此文件时,它会成功打开并指向文件句柄,但在读取nbins int时会出现分段错误错误。这是一个最小的例子:

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

#define BPATH "/path/to/file"

int main(int agrc, char **argv)
{
    FILE *fd;
    int num;
    char fname[500]={};

    int nbins;
    float *coords;

    num = 5;
    sprintf(fname,"%s/file%d.dat", BPATH, num);

    if(!(fd=fopen(fname,"rb")))
    {
        printf("Can't open file: %s\n\n",fname);
        exit(0);
    }    

    printf("Reading input file:\n");
    printf("%p: %s\n", fd, fname);       // prints successfully

    fread(&nbins, sizeof(int), 1, fd);   
    printf("nbins = %d", nbins);         // seg faults before this print

    /* EDIT: the above print isn't properly flushed without an \n    
     * The seg fault was not caused by the fread(), but the lack of  
     * the above print lead to the confusion                         */

    coords = malloc(nbins * sizeof(float));
    fread(coords, sizeof(float), nbins, fd);

    fclose(fd);
    free(coords);

    return(0);
}

该文件是使用以下格式创建的:

int nbins[1];
nbins[0] = 5;                          // this 5 is just an example...
fwrite(nbins, sizeof(int), 1, file_ptr);
fwrite(coords, sizeof(float), nbins[0], file_ptr);

我也试过用:

int *nbins = malloc(sizeof(int));
fread(nbins, sizeof(int), 1, fd);

但这并没有解决问题。该文件确实存在且可读;我可以使用Python和NumPy的fromfile()来读它。我错过了一些明显的东西吗谢谢!

c binaryfiles fread
1个回答
4
投票

你可能有undefined behavior,有以下场景:

  • int nbins;没有初始化nbins,所以它包含垃圾数据,可能是一个非常大的数字。
  • fread(&nbins, sizeof(int), 1, fd);没有经过测试,所以可能会失败,并保持nbins未初始化。了解fread
  • printf("nbins = %d", nbins);没有\n并且没有明确的fflush,所以不要显示任何东西(因为stdout通常是行缓冲的)。
  • coords = malloc(nbins * sizeof(float));会要求大量的内存,所以会失败并在NULL获得coords
  • 由于UB,fread(coords, sizeof(float), nbins, fd);写入NULL指针,给出了分段违规

你很幸运。事情可能是worse(我们都可能被黑洞歼灭)。你也可以尝试一些nasal demons,或者更糟糕的是,有一些似乎显然有用的执行。

下次,请避免UB。我不想在黑洞中消失,所以请耐心等待。

顺便说一句,如果你使用GCC,编译所有警告和调试信息:gcc -Wall -Wextra -g。它会警告你。如果没有,你将获得gdb调试器下的SEGV。在Linux上,valgrindstrace都可以提供帮助。

请注意,无用的初始化(例如,在您的情况下显式的int nbins = 0;)在实践中不会造成伤害。优化编译器可能会删除它们,如果它们没用(当它们没用时,就像你的情况一样,它们非常快)。

强制阅读

Lattner的博客:What Every C Programmer should know about UB。相关概念:As-if rule

另请阅读您正在使用的每个功能的文档(甚至像printf一样常见)。

© www.soinside.com 2019 - 2024. All rights reserved.