Home > database >  Designated Initialiser on struct causes segfault in strcpy and realloc
Designated Initialiser on struct causes segfault in strcpy and realloc

Time:02-22

I'm using WSL Ubuntu-20.04 LTS and compiling with gcc.

I'm preparing some examples to teach the basics of C to CS Students and as I was reading about designated Initialisers, the following bit of code caused errors,


    typedef enum {male, female} sexe;

    struct human_t {
        char * name;
        char * title;
        sexe gender;
    };

    struct human_t steve = {.name = "Steve", 
                              .title = "Mr.", 
                              .gender = male
                             };

    strcpy(steve.name, "AA");

And reading the manual on strcpy the destination buffer being of larger size than "AA" I'm simply at a loss as to why this doesn't work.

I also observed the following :

    struct human_t steve = {.name = "Steve", 
                              .title = "Mr.", 
                              .gender = male
                             };
    char * new_name = "Gobeldydukeryboo";
    realloc(steve.name, strlen(new_name));
    strcpy(steve.name, new_name);

And this returns the error realloc(): invalid pointer.

Reading the manual on realloc the pointer must have been returned by a malloc call for it to work. I'm having a feeling that designated initialisation does not call malloc, which would explain why realloc fails, it doesn't, however, explain why strcpy fails.

What's going on here ?

CodePudding user response:

struct human_t steve = {.name = "Steve", 
                          .title = "Mr.", 
                          .gender = male
                         };

Here name (and title) is initialized with the address of a string literal. String literals are not allocated from heap and in fact they are (usually) read-only. So string literals should always be assigned to const char* if you want to write more robust code.


So in the first snippet, strcpy(steve.name, "AA"); will try to write to read-only memory and crashes. If you had made steve.name a const char, it would have produced a compiler error and you would have been safe.


Then 2nd snippet, realloc(steve.name, strlen(new_name)); will try to resize a memory block which was not allocated with malloc. So you get a crash.

Also, this is not how you use realloc at all. You need to do it like this:

char *tmp = realloc(steve.name, strlen(new_name) 1);
if (!tmp) { perror("realloc error"); exit(1); }
strcpy(tmp, new_name);
steve.name = tmp;

To fix, store copy of the string:

struct human_t steve = {.name = strdup("Steve"), 
                          .title = strdup("Mr."), 
                          .gender = male
                         };

Here strdup is a POSIX function, but not standard C function, so you may need to define it yourself:

char *strdup(const char *src)
{
    char *dst = src ? malloc(strlen(src) 1) : NULL;
    if (dst) strcpy(dst, src);
    return dst;
}

CodePudding user response:

In this declaration

struct human_t steve = {.name = "Steve", 
                          .title = "Mr.", 
                          .gender = male
                         };

you initialized the pointer name with the address of the first character of the string literal "Steve".

Then in this statement

strcpy(steve.name, "AA");

you was trying to change the string literal.

Any attempt to change a string literal results in undefined behavior.

Instead you could just write

steve.name = "AA";

After that the pointer name will point to the string literal "AA".

In this statement

realloc(steve.name, strlen(new_name));

you are trying to reallocate the memory occupied by a string literal that has static storage duration. But you may reallocate only memory that was previously allocated dynamically. Moreover in general you need to assign to a pointer the returned address of a call of realloc. Otherwise the address of the reallocated memory will be lost and you will have a memory leak.

Either declare the data member name as a character array as for example

struct human_t {
    char name[10[;
    char * title;
    sexe gender;
};

Or always allocate dynamically a memory to store there a string as for example

struct human_t steve = {.name = malloc( 10 ), 
                          .title = "Mr.", 
                          .gender = male
                         };

strcpy( steve.name, "Steve" );

The same problem exists with another data member title.

  • Related