Unsuccessful fread() of int stored in binary file, segmentation fault [closed]
Asked Answered
H

1

0

There seem to be of the order of 10 questions and (mostly) successful answers solving segmentation faults cause by misused fread()'s in C. That being said, I am having such a problem but have not found a solution.

I have a binary file containing an int (call it nbins) and an array of floats (of size nbins). When I try to read this file, it successfully opens and points to the file handle, but then gives a segmentation fault error when reading the nbins int. Here is a minimal example:

#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);
}

The file was created with the following formatting:

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);

I have also tried using:

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

but this did not solve the problem. The file does exist and is readable; I can read it just fine using Python, with NumPy's fromfile(). Am I missing something obvious? Thanks!

Heall answered 2/10, 2017 at 13:26 Comment(12)
Read documentation of fread. Test its result (using perror on failure). Compile with all warnings and debug info gcc -Wall -Wextra -g. Use the debugger gdb & vagrind.Niu
Can you really reproduce the error with the exact code you have presented in the question? If so, how are you certain that the segfault occurs where you say it does? I would find that behavior very surprising, but I can think of some variations that would be less surprising.Sapro
I don't think the segfault is where you think it is.Fearsome
Let me amend the comment seg faults here. See new text!Heall
Now you should check the result of fread as suggested.Fearsome
I don't see anything obviously wrong in your code. But your example is not complete, the declaration of fname is missing and depending on how fname is declared you could get various problems.Satiable
You do not have \n in the printf after fread - so maybe you just do not see it?Lovel
It would be better to initialize nbins to 0.Niu
What is fname? Please provide a complete example.Hypostatize
^ditto + the input file.Fearsome
Yes I'm sorry I missed the declaration of fname in the minimal example. Should be complete, I will check if it does compile nowHeall
@ArtemyVysotsky yes, well spotted!Heall
N
3

You may have undefined behavior, with the following scenario:

  • int nbins; does not initialize nbins, so it contains junk data, potentially a very large number.

  • fread(&nbins, sizeof(int), 1, fd); is not tested so could fail and keep nbins uninitialized. Read about fread.

  • printf("nbins = %d", nbins); has no \n and is not followed by an explicit fflush so don't show anything (since stdout is usually line-buffered).

  • coords = malloc(nbins * sizeof(float)); would request a huge amount of memory, so would fail and get NULL in coords

  • fread(coords, sizeof(float), nbins, fd); writes to the NULL pointer, giving a segmentation violation, since UB

You are very lucky. Things could be worse (we all could be annihilated by a black hole). You could also experiment some nasal demons, or even worse, have some execution which seems to apparently work.

Next time, please avoid UB. I don't want to disappear in a black hole, so bear with us.

BTW, if you use GCC, compile with all warnings and debug info : gcc -Wall -Wextra -g. It would have warned you. And if it did not, you'll get the SEGV under the gdb debugger. On Linux both valgrind and strace could have helped too.

Notice that useless initialization (e.g. an explicit int nbins = 0; in your case) don't harm in practice. The optimizing compiler is likely to remove them if they are useless (and when they are not useless, as in your case, they are very fast).

Mandatory read

Lattner's blog: What Every C Programmer should know about UB. Related notion: the As-if rule.

Read also the documentation of every function you are using (even as common as printf).

Niu answered 2/10, 2017 at 13:43 Comment(3)
The lack of \n in the nbins print was relevant indeed. nbins is read in correctly, so the segmentation fault is occurring later. Thanks for your help!Heall
As my question has been shown to be incorrect in it's assertions, I will accept this as the correct answer (since it pointed out what was wrong with the question). The actual error causing the seg fault doesn't relate to the fread() of the intHeall
But seriously, work hard to avoid UB. I don't want to disappear in a black hole.Niu

© 2022 - 2024 — McMap. All rights reserved.