Feralheart
Feralheart

Reputation: 1920

fscanf on string causes segmentation fault

I have a three row input file. First row is an int, the second row is ints with space, the third row is a string.

I have to scan them than manipulate the string based on the ints.

My problem is that I can scan the ints, but scanning the string causes segmentation fault at fclose.

My code:

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

int main()
{
    FILE* in = fopen("be.txt", "r");
    FILE* out = fopen("ki.txt", "w");

    if(in==NULL){
        printf("Error opening in!\n");
        return -1;
    }
    if(out==NULL){
        printf("Error opening out!\n");
        return -1;
    }

    int brknglength, i;

    fscanf(in, "%d", &brknglength);

    printf("%d\n", brknglength);

    int* seed[brknglength];
    seed[brknglength] = malloc(sizeof(int[brknglength]));

    for(i = 0; i < brknglength; i++){
        if (fscanf(in, "%d", &seed[i]) != 1) {
            printf("%d", i);
        }
        printf("%d.: %d \n", i, seed[i]);

    }

    char string;

    fscanf(in, "%s", &string);


    free(seed[brknglength]);

    fclose(in);
    fclose(out);
    return 0;
}

What causes the segmentation fault?

Upvotes: 1

Views: 584

Answers (2)

Ralph
Ralph

Reputation: 345

Your first Problem appears here:

int* seed[brknglength];

This defines an array of int pointers on the stack.

seed[brknglength] = malloc(sizeof(int[brknglength]));

This initializes the element behind the array and overwrites your stack. To fix it, use either:

int seed[brknglength]; /* use without free(seed) */

or:

int *seed = malloc(sizeof(int[brknglength]));
/* ... */
free(seed);

The latter also works for compilers, which do not support variable length arrays.


Your second problem is reading a string into a single char variable, which also overwrites the stack. Try something like:

char string[100];
fscanf(in, "%99s", &string);

Be aware, that "%s" stops at whitespace. Use something like "%99[^\t\n]" to define your own separators, or "%99c" for a fixed length string.

The GNU Compiler offers the modifier "m" (=allocate memory) as a convenient non-standard extension for all these cases:

char *string;
fscanf(in, "%ms", &string);
/* ... */
free(string);

Upvotes: 2

Eric Postpischil
Eric Postpischil

Reputation: 222323

int* seed[brknglength];
seed[brknglength] = malloc(sizeof(int[brknglength]));

It looks like what you tried to do here is make seed a pointer to an array of int and allocate space for it. However, that is the wrong syntax. Because [ ] has higher precedence than *, int* seed[brknglength]; defines an array of pointers to int. Also, the name of the object is seed, not seed[brknglength], so you would assign a value to it with seed = …, not with seed[brknglength] = ….

To make a pointer to an array and allocate space for it, use:

int (*seed)[brknglength];
seed = malloc(sizeof *seed);

Those can be combined (which is not a violation of the above note about using seed = for assignment—initialization has a special syntax):

int (*seed)[brknglength] = malloc(sizeof *seed);

However, you probably do not want that. If size is a pointer to an array, then you have to use *seed wherever you want to refer to the array. So fscanf(in, "%d", &seed[i]) would have to be fscanf(in, "%d", &(*seed)[i]).

Instead of making seed a pointer to an array, just make it a pointer to an int, and allocate space for as many int as you want:

int *seed = malloc(brknglength * sizeof *seed);

Then you can use seed[i] for element i of the array instead of having to use (*seed)[i].

char string;

That defines string to be a single char. But fscanf(in, "%s", &string); reads as many characters as the input has until a white-space character. So you need to pass fscanf a pointer to the first of many char. You can either declare string to be an array:

char string[100];

or a pointer to space that is allocated:

char *string = malloc(100 * sizeof *string);

Then you can use fscanf(in, "%s", string);. Note that you do not want to pass &string. That is the address of the array or of the pointer, depending on how you defined string. You want to pass the address of the first character, which is &string[0], or, equivalently, string. (If string is an array, it is automatically converted in this expression to a pointer to its first element, so it is equivalent to &string[0].)

Note that fscanf will read as many character as the input contains until a white-space character appears. That can exceed whatever size you provide for string. So you need to ensure the input does not have too many characters or tell fscanf to limit how much it reads, which you can do with:

fscanf(in, "%99s", string);

or:

int n = 99;
fscanf(in, "%*s", n, string);

Note that fscanf should be told to read at most one character less than the space in string because it needs to add a terminating null character.

To free these objects, use:

free(seed);
free(string); // (If defined as a pointer, not an array.)

Upvotes: 2

Related Questions