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
.