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 ?
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;
}