1

I want to open a file, read its content and store it in an array using C code.

I did it on my Windows laptop and it works but when i try the code on my Raspberrypi i get segmentation faults. I've been trying for a while to debugm I'm quite new to C so I'm having trouble finding what I did wrong.

    char *readFile(char *fileName)
    {
        FILE *ptr_file;
        char *ptr_data;
        int n = 0;
        char c;

        ptr_file = fopen(fileName, "r");
        if(ptr_file == NULL)
        {
            perror("File could not be opened.\n");
            exit(EXIT_FAILURE);
        }
        fseek(ptr_file, 0, SEEK_END);
        long f_size = ftell(ptr_file);
        fseek(ptr_file,0, SEEK_SET);
        ptr_data = (char *)malloc(f_size+1);

if(ptr_data == NULL)
{
perror("MALLOC FAILED");
exit(EXIT_FAILURE);
}

        while((c = fgetc(ptr_file)) != EOF)
        {
                ptr_data[n++] = (char)c;

        }
        ptr_data[n] = '\0';
        fclose(ptr_file);
        return ptr_data;
    }

to me it seems like the segmentations fault appears in the while loop after the call to malloc.

Why does it work on my laptop and not on the raspberrypi?

at the same time i dont understand why i get segmentation faults on my RPi if id do this:

   int main(int argc, char *argv[])
            {
    char data[100] = {};
                FILE *ptr_file;
                char *ptr_data=data;
                int n = 0, i = 0;
                char c;

                ptr_file = fopen(fileName, "r");
                if(ptr_file == NULL)
                {
                    perror("File could not be opened.\n");
                    exit(EXIT_FAILURE);
                }

                while((c = fgetc(ptr_file)) != EOF)
                {
                        ptr_data[n++] = (char)c;

                }
                ptr_data[n] = '\0';
                while(i <n,i++)
    {
    printf("%c\n",data[i]);
    fclose(ptr_file);

        }

return 0; }

Arro
  • 7
  • 2
  • 4
    Check that the allocation actually succeeds. Also, [don't cast the return of `malloc`](http://stackoverflow.com/a/605858/440558). – Some programmer dude Feb 04 '14 at 10:42
  • check `f_size` must match the actual file size. – BLUEPIXY Feb 04 '14 at 10:44
  • 4
    Reading a file char by char with `fgetc` is very inefficient. You'd better use `fread`. – Didier Trosset Feb 04 '14 at 10:44
  • 5
    `char c;` --> `int c;` – BLUEPIXY Feb 04 '14 at 10:46
  • How can I do a check on f_size? more then knowing my own file size that is. also i use fgetc to eventually clear some specific chars.. edited my allocation check, forgot to put it in there. why should c be an int? and the casting, i guess ptr_data = malloc(sizeof*ptr_data * (f_size +1)) should work instead – Arro Feb 04 '14 at 11:19
  • By looking at it in the debugger, and verifying that it matches the size you can see with ls. – Devolus Feb 04 '14 at 11:24
  • Where is your main? How can you be sure that this code causes the segfault? It looks ok to me, if ftell doesn't fail. – Devolus Feb 04 '14 at 11:26
  • I would use [fread(3)](http://man7.org/linux/man-pages/man3/fread.3.html) e.g. `size_t sz = fread(ptr_data, f_size, 1, ptr_file);` and use the `"rb"` mode for [fopen(3)](http://man7.org/linux/man-pages/man3/fopen.3.html) – Basile Starynkevitch Feb 04 '14 at 11:42
  • because my main for now is basicly input = readfile(text.txt); it seems ftell doesnt match my intended file length, it seems to be larger. – Arro Feb 04 '14 at 11:47
  • How big is your file? Did you install Linux on your laptop to test your software under Linux? – Basile Starynkevitch Feb 04 '14 at 12:25
  • my file is very small, its a kinda small project for me to get to know both linux and c.. i want to use it on my raspberrypi thats why i use linux – Arro Feb 04 '14 at 13:48

2 Answers2

2

There are some issues when reading text file on different environment. When writing a new line, for example, may consume 2 bytes on Windows, and just 1 on Linux. From an article:

Subclause 7.21.9.4 of the C Standard [ISO/IEC 9899:2011] specifies the following behavior for ftell() when opening a text file in text mode: For a text stream, its file position indicator contains unspecified information, usable by the fseek function for returning the file position indicator for the stream to its position at the time of the ftell call. Consequently, the return value of ftell() for streams opened in text mode should never be used for offset calculations other than in calls to fseek().

In another words, fseek and ftell function behavior may be different depending on which environment you're working with.
For further explanation you may read this topic: https://www.securecoding.cert.org/confluence/display/seccode/FIO14-C.+Understand+the+difference+between+text+mode+and+binary+mode+with+file+streams

rfermi
  • 179
  • 11
0

Maybe you should disable Linux memory overcommit. See this.

BTW, you might consider using open(2) to open your file, fstat(2) to get statistics, notably file size, about it, then mmap(2) to project the file into virtual memory by growing your address space.

int fd = open(fileName, O_RDONLY);
if (fd<0) { perror(fileName); exit(EXIT_FAILURE); };
struct stat fs;
memset (&fs, 0, sizeof(fs));
if (fstat(fd, &fs)) { perror("fstat"); exit(EXIT_FAILURE); };
long pgsz = sysconf(_SC_PAGE_SIZE); // a power of two
size_t roundedsz = (fs.st_size | (pgsz-1)) + 1; // round up the size
void* ad = mmap(NULL, roundedsz, PROT_READ, MAP_SHARED, fd, (off_t)0);
if (ad == MMAP_FAILED) { perror("mmap"); exit(EXIT_FAILURE); };

Then use ad instead of ptr_data (which becomes useless). Don't forget to call munmap and close when you are done...

You could close just after the mmap if you want to.

Read Advanced Linux Programming.

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547