Reputation: 37
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
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
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
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
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