arrayscmemorycallocfault

Segmentation fault: 11 while trying to print array with 50k items


I'm trying to print array with 50k items into a file but it could be done only if I set small numbers of items, e.g. 5k.

void fputsArray(int *arr, int size, char *filename)
{
    char *string = (char*)calloc( (8*size+1), sizeof(char) );
    for(int i = 0; i < size; i++)
        sprintf( &string[ strlen(string) ], "%d\n", arr[i] );

    FILE *output;
    char fullFilename[50] = "./";
    output = fopen(strcat(fullFilename, filename), "w");
    fputs(string, output);
    fclose(output);
    free(string);
}

size is 50000, defined in #DEFINE. This is working code. But if I delete 8 multiplying to size, that is I supposed to be working, doesn't work. I got in that situation Segmentation fault: 11 Why should I allocate 8 times more memory than I need?


Solution

  • Assuming the input to your function is all correct:

    void fputsArray(int *arr, int size, char *filename)
    

    Sizes should be given as size_t.

    {
        char *string = (char*)calloc( (8*size+1), sizeof(char) );
    

    The clearing of the memory (calloc) is unnecessary, malloc and setting string[0] = '\0' would suffice. sizeof( char ) is always 1 by definition. And you should not cast the result of an allocation.

    Actually, the whole construct is unnecessary, but that's for later.

        for(int i = 0; i < size; i++)
            sprintf( &string[ strlen(string) ], "%d\n", arr[i] );
    

    Not actually that bad, aside from string + strlen( string ) being simpler and that there should always be { } surrounding the statement. Still unnecessarily complex.

        FILE *output;
        char fullFilename[50] = "./";
        output = fopen(strcat(fullFilename, filename), "w");
    

    A filename is always relative to the current working directory, so the "./" is unnecessary. You should however have checked the filename length before strcating it into a static buffer like that.

        fputs(string, output);
    

    Ah, but you have not checked if the fopen actually succeeded!

        fclose(output);
        free(string);
    }
    

    All in all, I've seen worse. Whether your numbers actually fit your buffer is guesswork, though, and most importantly the whole memory shenanigans are unnecessary.

    Consider:

    void printArray( int const * arr, size_t size, char const * filename )
    {
        FILE * output = fopen( filename, "w" );
        if ( output != NULL )
        {
            for ( size_t i = 0; i < size; ++i )
            {
                fprintf( output, "%d\n", arr[i] );
            }
            fclose( output );
        }
        else
        {
            perror( "File open failed" );
        }
    }
    

    I think this is much better than trying to figure out where your memory guesswork went wrong.


    Edit: On second thought, I would have that function take a FILE * argument instead of a filename, which would give you the flexibility of printing to an already-opened stream (like stdout) as well, and also let you do the error handling of the fopen in a place higher up that might have additional capabilities to give useful information.