Zg Ma Zg Ma - 3 months ago 11
C Question

what's difference between struct->char_member = "" and strcat(struct->char_member,"string")?

I have the following code:

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

struct test {
char *str;
};

int main(void)
{
struct test *p = malloc(sizeof(struct test));
p->str = "hello world";
printf("%s\n",p->str);
return 0;
}


It works fine. But when I write like this:

struct test *p = malloc(sizeof(struct test));
p->str="hello";
strcat(p->str," world");


or this:

struct test *p = malloc(sizeof(struct test));
strcat(p->str,"hello world");


I got a segmentation fault.

And I found some relevant explanation here:


You are allocating only memory for the structure itself. This includes the pointer to char, which is only 4 bytes on 32bit system, because it is part of the structure. It does NOT include memory for an unknown length of string, so if you want to have a string, you must manually allocate memory for that as well


Allocate memory for a struct with a character pointer in C

With the explanation, I know the correct code should be like this if I want to use strcat:

struct test *p = malloc(sizeof(struct test));
p->str = malloc(size);
strcat(p->str,"hello world");


So my question is why p->str = "hello world" does not need allocate memory but strcat(p->str,"hello world") needs allocate memory before use?

My compiler is "gcc (Debian 4.9.2-10) 4.9.2". My English is pretty basic, please don't mind :)

Answer

"hello, world", "hello", and " world" are all string literals; they are all stored as arrays of char such that they are available as soon as the program starts and are visible over the lifetime of the program.

The statement

p->str = "hello, world";

copies the address of the string literal to p->str. This works fine for the printf statement and anything else that just needs to read the string. However, in the statements

p->str = "hello";
strcat( p->str, " world" );

you are trying to modify the string literal "hello" by appending the string " world" to it. String literals are not modifiable, and attempting to do so leads to undefined behavior, which in your case is a segfault - on many popular desktop platforms, string literals are saved in a read-only section of memory.

Therefore, you need to set aside a region of memory that you can write to. You can either do it dynamically with

p->str = malloc( SOME_SIZE);  // enough space to store your final string
strcpy( p->str, "hello" );  // copy the contents of "hello" to the memory str points to
strcat( p->str, " world" ); // append the contents of " world" to the memory str points to

or you can set p->str to point to an array you've declared elsewhere

char buffer[SOME_SIZE];
p->str = buffer; // assigns the *address* of buffer to p->str

or you can declare str as an array of char in the struct definition:

struct test
{
  char str[SOME_SIZE];
};

where SOME_SIZE is big enough to hold whatever string you want to store. Note that in this case, p->str = "hello" won't work; you can't use the = operator to assign the contents of arrays to each other; you must use strcpy in that case.

Obviously, dynamic allocation with malloc or calloc is more flexible; you can allocate exactly as much memory as you need, and you can grow or shrink the dynamic buffer as necessary using realloc You just need to remember to free p->str when you're done.

You can still assign the address of a string literal to p->str, just be aware that you cannot pass that address to functions like strcpy or strcat or strtok or any other functions that attempt to modify the contents of the input string.