I have a struct
struct request {
int code;
char *message;
};
that I'd like to free properly.
I have the following function to do that:
void free_request(struct request *req) {
if (req->message != NULL) {
free(req->me开发者_如何学Gossage);
}
free(req);
req = NULL;
}
The problem is that I get an "free(): invalid pointer"/segfault error from the compiler when I try to free a request that has been created using a string literal:
struct request *req;
req = malloc(sizeof(struct request));
req->message = "TEST";
free_request(req);
Since I want to create request structs in different places, once using literals (on the client side) and once using *chars that I read from a socket (on the server side) I was wondering if there is a function to make sure that I don't try to free the literals while still allowing me to free the message I have created using a malloc.
There is no standard function that lets you know if a pointer was dynamically allocated or not. You should include a flag in your struct to inform yourself of it, or only use dynamically allocated strings (strdup
is your friend in this case). Depending on your networking setup, it might be simpler to use strdup
(well, to tell the truth, it is simpler to use the strdup
at all).
With strdup
:
struct message* req;
req = malloc(sizeof *req);
req->message = strdup("TEST");
free_request(req);
With a flag:
struct message
{
int code;
char* message;
bool isStatic; // replace to 'char' if bool doesn't exist
};
void free_request(struct message* req)
{
if (!req->isStatic) free(req->message);
free(req);
}
struct message* req;
req = malloc(sizeof *req);
req->message = "TEST";
req->isStatic = 1;
free_request(req);
Also, don't forget to zero your allocated memory when you create an object. That could save you a lot of trouble.
req = malloc(sizeof *req);
memset(req, 0, sizeof *req);
That, and setting req
to NULL
from free_request
won't have any effect. You either need to take a struct message**
or do it yourself after the function calls.
There is no way to tell if you are using a string literal (Well, you can put string literals in a custom .section created by GCC, and then examine the string pointer to determine if it is contained in the .section of literals). However...there is a better way using a simple programming pattern.
Allocation With Literal
Normal case. A call to free(req) will work as expected: freeing the request structure.
struct *req;
req = malloc(sizeof(*req));
req->message = "TEST";
Allocation With Dynamic String
In the following, some_string
is a string you wish to store as the request message. It can be either a literal, or a dynamically allocated. This allocates memory for the string when the struct itself is allocated (and will be freed automatically when the struct is freed).
struct *req;
req = malloc(sizeof(*req)+strlen(some_string)+1);
req->message = (char *)&req[1];
strcpy(req->message, some_string);
Freeing
free(req);
Edit: General Case
Note that the allocation scheme above for the dynamic string
is general, it can be used even when you don't know if some_string
is a literal or not. Thus, a single function that takes care of both cases, and freeing with free()
rids you of special cases.
I would suggest adding a member to struct request
to indicate whether request::message is dynamically allocated, and set that member at the same time you assign request::message
, then check it before releasing the memory. It's a bit messy in C.
Note that it is not only string literals that would cause the problem, any pointer to data not dynamically allocated on the heap by malloc() or calloc() would fail, so simply detecting "if a char points to a string literal in C"* even if it could be done portably would not help.
It segfaults because the memory location containing "TEST"
is (usually) read-only and not located on the heap (usually because it resides in some read-only section of the program). Given only a char*
pointer, you won't be able to know if it points to a free()
-able string or not. Instead, you should allocate a buffer for req->message
and copy the characters.
char* str = "TEST";
int len = strlen(str);
req->message = malloc(len+1);
strcpy(req->message, str);
Or, you can use strdup()
as suggested by zneak.
If you're just trying to make sure that malloc'ed memory is freed, you can call
realloc(req->message,(size_t)0)
If the memory library implementation is robust, it should work.
Have a look at:
struct request *req;
req = calloc(1,sizeof(struct request));
strcpy(req->message = malloc(strlen("TEST")+1),"TEST");
free_request(req);
Its strictly ANSI C conform. strdup isnt ANSI C.
req = NULL;
is redundant.
精彩评论