I have a function which reallocs a pointer given as an argument to a new size. Now, the problem is that - according to the man page - realloc
needs a pointer which has been returned by malloc
or calloc
before.
How can I make sure that the caller passes a pointer that meets those requirements? There seem to be no build-in C mechanics (like type qualifiers or somethi开发者_如何学Gong) to do so.
Now, before I restructure my API (as I consider the function as it is now not to be robust enough) - can you please verify that I haven't missed something?
Thanks in advance.
Edit: One solution would obviously be to malloc in the function. The problem with that is that the caller does not "see" the allocation. Thus I would need to explictly say in the docs that he has to free the pointer. That's even worse than to expect them to provide a malloc'd pointer (which would imply that the caller has to free it).
What I really want is something that blocks abuse at compile time. That, and a pony. ;-)
How can I make sure that the caller passes a pointer that meets those requirements?
Documentation.
Define the API.
If the person writing the caller refuses to follow the API, things crash. They refused to play by your rules and things crashed. What did they expect?
At the end of the day, pointers in C are nothing but a machine word, which may or may not point to maningful data inside the process's memory, which may or may not be allocated by malloc. There is no such information attached to any pointer.
Just add a big, fat warning to the API. If the documentation clearly says "never ever pass this function pointer which wasn't malloc'd", and someone does it nonetheless and gets bugs/crashs, it's not your fault - there's no way you can program defensively here.
You cannot determine whether the area a pointer points to has been [m|c]alloc();
'd. You can only define your API and hope your API users follow it.
Is allocating memory yourself (callee) prohibitive? You can still do the initial allocation with realloc(NULL, size);
if you're bound by that.
More information on your problem would be nice.
One possible way. Abstract it.
foo.c
#include "foo.h"
struct foo{
...
} ;
foo *newFoo(...)
void resize(foo *f)
foo.h
struct foo;
typedef struct foo;
caller.c
foo *myfoo;
myfoo = newFoo(..)
resize(myfoo);
How is the user supposed to obtain this malloc'ed pointer in first place? Isn't that possible that it is obtained by another function in your API? Otherwise it sounds like boilerplate-code to me, having to allocate a chunk of memory with malloc before calling your function.
In your situation I would try that. Having the caller obtain a valid pointer trough a function I provide and releasing it trough another function. Even better, wrap it all together in an opaque structure.
my_param_type my_param = init_param(....);
...
my_function_that_wants_malloc(.....,my_param);
...
free_param(my_param);
Does that make sense in your API?
Having its own type it will be clear even to the most lame user that he has to obtain it through the expected init_param() function.
Change your implementation so you don't use realloc. If the buffer passed in is too small, just malloc a new bigger one and memcpy the content of the passed in buffer to it. This will improve the usability of your API at the expense of a marginal loss of performance.
If all you have is the pointer value, there is no (standard) way to determine if that pointer value was returned by malloc()
. If you have deeply intimate knowledge of how dynamic memory allocation works on your platform, then you can make an educated guess based on the pointer value, but even that's not a guarantee you're looking at something that was actually a return value from malloc()
as opposed to a value offset from that (e.g., *ptr = malloc(10); foo(&ptr[5]);
).
One strategy that may or may not be worth the effort (IME, it isn't) is to hide malloc()
, realloc()
, and free()
behind some kind of memory manager that keeps track of allocated pointers and sizes. Instead of calling realloc()
, your code would call the resize function in the memory manager, which would return an error code if the pointer passed to it isn't in the list of managed pointers.
You can maintain a static pointer within your function and call the realloc yourself. Return the static pointer. This way the user does not have worry with allocation or freeing. The variable will be freed automatically when the process dies.
Be aware that if realloc fails, it returns a null pointer but the original malloc'ed memory is left untouched, this is a common source of memory leaks.
Create a custom data type that is a renamed void pointer.
typedef void* myPointerType;
Write an allocate function (that can be a simple wrapper around malloc
) that the user must use to allocate memory.
myPointerType myAllocateFunction (size_t size) {
return (myPointerType)(malloc(size));
}
You would also want to make a matching "free" function.
Now, have your function (the one that performs the realloc
) take a myPointerType
object as a parameter instead of a generic pointer. You can cast it back down to a void*
to use with realloc
. To get around this, the user would have to manually cast a non-malloc
ed pointer to a myPointerType
, but you can specify in your documentation that casting to and from myPointerType
is not allowed (so if they do this and their app crashes, it's because they misused the API).
There are even stronger ways that you can enforce this, but I'm not sure if it would be worth the trouble. You can't completely fool-proof an API (you'd be amazed at how capable fools are these days). Whatever you end up implementing, the best way to ensure that your API is used correctly is to provide clear, comprehensive documentation.
You're on your own for the pony, sorry.
If you're looking for a compile-time check, the only thing you can really do is declare some new type which is returned by "approved" allocation calls. I would suggest something like:
typedef struct MEM_INFO {void *ptr; int allocsize; struct *privateMemInfo priv;} ALLOCMEM[0];
The privateMemInfo would be defined in your .c file, rather than the header, thus somewhat protecting fields in it from mischief. Note that since ALLOCMEM would be an array type, ptr and allocsize would always be accessed using the arrow operator rather than the dot. A routine which takes an ALLOCMEM as a parameter would naturally be given a pointer to the ALLOCMEM, allowing it to perform relocation "nicely". Thus, for example, one would use:
ALLOCMEM foo = {0}; if (reallocate(foo,12345) >= 0) /* It worked */
Thus I would need to explictly say in the docs that he has to free the pointer. That's even worse than to expect them to provide a malloc'd pointer (which would imply that the caller has to free it).
In C libraries it's a common practice:
- http://library.gnome.org/devel/glib/stable/glib-String-Utility-Functions.html#g-strconcat
- http://library.gnome.org/devel/gtk/unstable/GtkEntry.html#gtk-entry-get-icon-tooltip-text
- http://www.xmlsoft.org/html/libxml-tree.html#xmlNodeGetContent
精彩评论