Date: Fri, 8 Jun 2001 16:36:45 -0700 (PDT) From: Matt Dillon <dillon@earth.backplane.com> To: Peter Pentchev <roam@orbitel.bg> Cc: John Baldwin <jhb@FreeBSD.ORG>, hackers@FreeBSD.ORG Subject: Re: free() and const warnings Message-ID: <200106082336.f58Naj299417@earth.backplane.com> References: <20010607195634.I724@ringworld.oblivion.bg> <XFMail.010607102051.jhb@FreeBSD.org> <20010608114957.C19938@ringworld.oblivion.bg>
next in thread | previous in thread | raw e-mail | index | archive | help
:OK, here's a scenario: : :struct validation_fun { : const char *name; : valfun *fun; : int dyn; :}; : :This is a structure for a set of validation functions, referenced by :name from another type of object. There are some predefined functions, :present in the code at compile-time, and hardcoded in an array, with :names given as "strings". Thus, the 'const'. : :However, some of the functions may be defined at runtime, with both :name and code sent by a server. In that case, the name is a dynamically :allocated char *, which needs to be freed upon cleanup. So I have: : :[cleanup function] : ... : if (val->dyn) : free(val->name); : :Any suggestions on how to improve the design to avoid this, if possible, :would be greatly welcome. : :G'luck, :Peter I am a great believer in const char * ... it's saved my ass on more occassions then I can count. I have very similar situations where certain elements of a structure are usually static but sometimes dynamic. My recommendation is that you NOT attempt to overload the duel nature of 'name'. Instead make it always a 'const char *'. Here is my suggestion: struct validation_fun { const char *name; valfun *fun; int dyn; /* * OPAQUE */ char *allocations; /* or make it an array for more */ }; As far as 99.9% of your codebase is concerned, 'name' points to a read-only entity and is not dynamically allocated, even from the point of view of FreeValidationFun(). But if you need to dynamically allocate the string for name you have a field to hold the dynamic allocation, in this case 'allocations'. You ALSO assign the result to 'name', but FreeValidationFun() only looks to 'allocations' to determine if there is anything it must free() and, of course, 'allocations' can remain NULL. This neatly solves the problem and guarentees that no confusion will occur. Here's another example: Lets say you have a routine that can return an error code AND an error message, and to make it fast you want to be able to return static strings for the message. But sometimes the error message is allocated. So: char *alloc = NULL; const char *emsg; error = TheRoutine1(parameters, &emsg, &alloc); ... process emsg ... error = TheRoutine2(parameters, &emsg, &alloc); ... process emsg ... error = TheRoutine3(parameters, &emsg, &alloc); ... process emsg ... safe_free(&alloc); TheRoutine*() assigns the error message simply with something like: *emsg = "Yah, Everything is fine"; But if TheRoutine*() needs to allocate the error message it does this: safe_replacef(alloc, "You screwed up %d times!", n); *emsg = alloc; I have three helper routines to make this sort of string management trivial: void safe_free(char **ptr); If *ptr is not NULL, free(*ptr) then set *ptr to NULL. void safe_replace(char **ptr, const char *str); If *ptr is not NULL, free(*ptr) then set *ptr = strdup(str); void safe_replacef(char **ptr, const char *ctl, ...); If *ptr is not NULL, free(*ptr) then use varargs and vasprintf() to allocate the replacement string. I call these routines 'safe' because if their internal malloc()s fail they assert and exit... I don't like checking for memory failures in thousands of places in my code, I prefer to check them in just a handful of places. Probably a more involved answer then you were looking for, but hey! BTW, I don't use static buffers for virtually anything any more... I use asprintf() (or in my case, safe_asprintf()), safe_strdup(), safe_replace(), safe_replacef(), safe_free(), etc... My 'safe' routines. I use them for everything these days. It removes all possibilities of a buffer overflow from my code. -Matt To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-hackers" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200106082336.f58Naj299417>