开发者

Howto check if a char* points to a string literal in C

开发者 https://www.devze.com 2023-01-09 21:24 出处:网络
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:

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.

0

上一篇:

:下一篇

精彩评论

暂无评论...
验证码 换一张
取 消