0

I have written a function which reads a file full of text, and proceeds to concatenate all the lines into a single string. It works, but the fclose() instruction, when used, launches an error :

"* Error in `./main': double free or corruption (out): 0x00000000020fc330 *" followed by a backtrace and a memory map.

It also works badly for files with a single line.

What can I do ?

The code :

char* readFile(char* fileName){
FILE* myFile = fopen(fileName, "r");
if (myFile != NULL) {
    fseek(myFile, 0, SEEK_END);
    long l = ftell(myFile);
    fseek(myFile, 0, SEEK_SET);
    char* content = (char*)malloc((size_t)l * sizeof(char));
    if (content == NULL) return NULL;
    char * chain = (char*)malloc((size_t)l * sizeof(char));     
    while(fscanf(myFile, "%s\n", chain) != EOF || fscanf(myFile, "%s\t", chain) != EOF || fscanf(myFile, "%s ", chain) != EOF){
        strcat(content, chain);
    }
    free(chain);
    fclose(myFile);         
    return content;
} else return NULL;

}

Micawber
  • 707
  • 1
  • 5
  • 19
  • The error is telling you about `free`. why are you asking about files? – Eugene Sh. Apr 04 '17 at 19:23
  • Try `malloc` with `l+1` elements. You need to have space for the terminating null character. Next, replace `strcat` with `strcpy`. You are concatenating `chain` to `content`, but `content` is not initialized, and thus you don't know where the function will actually write your data (it writes where it finds the first `null` character, but that could be anywhere). – Cris Luengo Apr 04 '17 at 19:30
  • `char* content = malloc(...); .. strcat(content, chain);` attempts to concatenate to uninitialized memory of `content` --> UB. – chux - Reinstate Monica Apr 04 '17 at 19:34
  • when asking a question about a run time problem. post code that cleanly compiles, run, is short, and still exhibits the problem. – user3629249 Apr 05 '17 at 15:39
  • variable names should indicate `content` or `usage` (or better, both). Variable names like `l` are meaningless, even in the current context. – user3629249 Apr 05 '17 at 15:42
  • when calling any of the heap allocation functions: (malloc, calloc, realloc) do not cast the result. The return type is `void*` which can be assigned to any other pointer. Casting just clutters the code, making it more difficult to understand, debug, etc. Always check (!=NULL) the returned value to assure the operation was successful. – user3629249 Apr 05 '17 at 15:43
  • for ease of readability and understanding: 1) follow the axiom: *only one statement per line and (at most) one variable declaration per statement.* 2) separate code blocks (for, if, else, while, do...while, switch, case, default) via a single blank line. 3) consistently indent the code. indent after every opening brace '{'. unindent before every closing brace '}'. Suggest using 4 spaces for each indent level as that is visible even with variable width fonts. – user3629249 Apr 05 '17 at 15:46
  • the expression: `sizeof(char)` is defined in the standard as 1. Multiplying anything by 1 has no effect. As part of the parameter to `malloc()` it just clutters the code, making it more difficult to understand, debug, etc – user3629249 Apr 05 '17 at 15:49
  • when calling any of the `scanf()` family of functions, 1) when using the '%s` input/conversion specifier, always use a MAX CHARACTERS modifier that is one less than the length of the input buffer to avoid any buffer overflow. Buffer overflow results in undefined behavior and can lead to a seg fault event. – user3629249 Apr 05 '17 at 15:53
  • this statement: `while(fscanf(myFile, "%s\n", chain) != EOF || fscanf(myFile, "%s\t", chain) != EOF || fscanf(myFile, "%s ", chain) != EOF)` will (almost) always fail. Suggest using: `fgets()` – user3629249 Apr 05 '17 at 15:56
  • when allocating room on the HEAP for the file contents, Remember to add an extra byte to the size of the allocation, because (amongst other reasons) the call to `strcat()` will append a NUL termination character. – user3629249 Apr 05 '17 at 15:57
  • `fseek(myFile, 0, SEEK_END);` is not guaranteed to work on a binary file (and `ftell()` on a text file provides no data on how many bytes are in the file). There is no portable, strictly-conformant way in C to get the size of a file. I'm not sure why such usage is so prevalent. – Andrew Henle Apr 05 '17 at 16:20

4 Answers4

0

Most likely what's happening here is that your calls to scanf() and/or strcat() are writing past the end of your allocated buffers (content or chain) and corrupting the heap. That corruption is noticed by free() when it tries to free 'chain', causing the error message and the crash.

In order to avoid writing past the end of the buffers, you'll need to know exactly how many bytes each of these calls is going to write into the buffer. That's difficult to know with fscanf() unless you have a guarantee on what's going to be in the file (and usually you do not), so you might want to use fread() instead, so that you can specify the maximum number of bytes to read. Also when calling strcat() you need to make sure that the buffer pointed to by the first argument has enough space for both its existing contents plus the contents of the string you want to add to the end of its existing contents. Consider calling strncat() instead if you want to make sure to avoid an overwrite (since strcat() has no way of knowing how big the buffers are, it will happily write past the end of a too-short buffer -- at least strncat() will stop writing after the number of bytes you specified in its third argument)

Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
0

In addition to othjer comments, you should avoid using strcat() in the loop. Each time through the loop, strcat has to search the string for the nul character, and this search keeps getting longer and longer.

A more efficient way would be to keep a pointer to the nul character and just use strcpy to add to it, then increment the pointer by the number of characters that were added.

FredK
  • 4,094
  • 1
  • 9
  • 11
0

As others have stated, your content pointer points to memory that is not initialized, it may contain junk when you are trying to strcat() to it. Try using calloc() instead, this will set all the chars to '\0' and allow you to do what you want.

Consider following Jeremy's advice and create a more efficient function to do what you want. Right now you're allocating two buffers the size of the file and looking for tokens to fill in one of these huge buffers and concatenating them into the other. You'll definitely have plenty of space since you're dropping the delimiters, but that is a lot of wasted space.

V. Sim
  • 101
  • 5
0

the posted code, as indicated in the comments to the question, has a lot of problems.

The following posted code corrects these problems, cleanly compiles, and performs the desired function.

#include <stdio.h>   // fopen(), fclose(), fseek(), NULL, perror()
#include <stdlib.h>  // malloc(), free()
#include <string.h>  // strcat()

char* readFile( char* );

char* readFile( char* fileName )
{
    FILE* myFile = fopen(fileName, "r");

    if ( !myFile )
    {
        perror( "fopen for reading file failed" );
        return NULL;
    }

    // implied else, fopen successful

    if( 0 != fseek(myFile, 0, SEEK_END) )
    {
        perror( "fseek to end of file failed:" );
        return NULL;
    }

    // implied else, fseek successful

    long fileLength = ftell(myFile);
    if( -1 == fileLength )
    {
        perror( "ftell failed" );
        return NULL;
    }

    // implied else, ftell successful

    if( 0 != fseek(myFile, 0, SEEK_SET) )
    {
        perror( "fseek to start of file failed" );
        return NULL;
    }

    // implied else, fseek successful

    char* content = malloc((size_t)fileLength+1 );
    if (content == NULL)
        return NULL;

    // initialize the concatenated string to be empty
    content[0] = '\0';

    char * chain = malloc((size_t)fileLength+1);
    if ( chain == NULL)
        return NULL;

    while( fgets( chain, (int)fileLength, myFile ) )
    {
        strcat(content, chain);
    }

    free(chain);
    fclose(myFile);
    return content;
} // end function: readfile
user3629249
  • 16,402
  • 1
  • 16
  • 17
  • another way to get the size of a file is to call `stat()` on the file then check the `st_size` field of the `struct stat` to get the total number of bytes in the file. – user3629249 Apr 05 '17 at 20:53