Zg Ma
Zg Ma

Reputation: 37

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 :)

Upvotes: 1

Views: 208

Answers (4)

John Bode
John Bode

Reputation: 123578

"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.

Upvotes: 1

Forrest
Forrest

Reputation: 236

In the first example, you are assigning an immutable string to p->str (this is stored in read-only memory). Thus trying to change the memory results in a seg-fault.

In the second example, you are failing to allocate space for p->str using malloc. Since p is uninitialized, you are reading from some random place in memory you don't own.

Try:

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

Here we have malloc'd just enough space for "hello world" (plus the '\0' character). strcpy copies the string "hello" to the place in memory you have allocated. Now strcat succeeds because you "own" the memory that p->str points to.

Also don't forget to free() memory that you have malloc'd!

EDIT: As a side note, I think you may be getting confused about allocating memory for the struct. In this case allocating memory for the struct only gives you enough memory for a char *; however you have not allocated the memory for the actual pointer to point to.

It is actually unnecessary to allocate memory for struct test, and you could get away with only allocating memory for the char *. Better would be:

struct test p;
p.str=(char *)malloc(12 * sizeof(char));
strcpy(p.str, "hello");
strcat(p.str," world");

Upvotes: 1

zneak
zneak

Reputation: 138251

To be clear, assigning to p->str is not what requires an allocation. p->str is just a memory address, and you can put in any memory address, valid or not. You need an allocation when you want to manipulate the content of the memory address.

In the case of p->str = "hello world", the compiler reserves ahead of time a region of memory and puts the "hello world" string in it, and uses that address.

Notice how the second argument to strcat is a string literal too, and it didn't need to be allocated.

Upvotes: 1

md5
md5

Reputation: 23727

You're right. But you should use strcpy instead of strcat here, because strcat will append a string to p->str, which is uninitialized.

In fact p->str = "hello world"; will make p->str point to an (anonymous) string litteral. The memory for it is typically allocated automatically at the beginning of the execution of the program.

Upvotes: 0

Related Questions