So, I have some function returning a pointer, or NULL
on error. Now I'd like to add another possible value for a different error/condition. I heard this mentioned in some other answer before - using malloc()
to create a unique pointer that will serve as a possible value for such functions to return (so now the can return a proper pointer, NULL
or 0xWhatever
and you can be sure 0xWhatever
won't be used for anything else). So, naturally malloc(1)
is probably a safe bet, but I was wondering if malloc(0)
is also safe for this. Will a malloc(0)
address possibly be used for something else? Can someone clarify on how this technique should work in general, and maybe what it is called?
views:
114answers:
6It is my understanding that in general, malloc(0)
will "allocate" the memory which should still be freed later. It will give you a location in memory which you shouldn't write to.
You don't necessarily have to use malloc()
to obtain a unique address, it could come from a static location for example. Then you could use this address as your other condition. Also leaves out the allocation as well. I don't recall the term for this use, something about a proxy.
#define ERRORPTR ((void *)&_unusedvar)
static char _unusedvar;
if ( /* failed */ )
return ERRORPTR;
From the C99 standard:
If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.
You're better off using malloc(1)
. That's guaranteed to return non-NULL if the memory is available.
Up to and I believe including C89, the meaning was unspecified. The standard simply had nothing to say about a non zero (or worse, negative) size. I'm reasonably sure it is now Implementation Defined. Use a non-zero size for portability.
You can evade the question by using the address of some global or static variable as a sentinel, perhaps with a suitable cast.
Another traditional approach (used in the earliest versions of signal()
, IIRC) was to cast small integers other than 0 to the pointer type. This is actually relatively non portable, as the only integer that portably and safely casts to a pointer is 0, which is NULL.
Note that whether allocated or not, you would need to be careful never to attempt to actually free your sentinel pointer.
Somewhat tangential to the question, but having an "error condition pointer" (other than NULL) isn't really idiomatic C. If you want to write a function which allocates memory to a pointer and can return several distinct error conditions, there are two more usual ways to do it:
- Set
errno
to describe your specific error, and returnNULL
for all errors. - Take a pointer-to-pointer as an argument, and return an integer error code
E.g.
sometype* my_function()
{
sometype* p;
/* Do something */
if (error_1_occurred) {
errno = EAGAIN; /* Or whatever is appropriate */
return NULL;
} else if (error_2_occurred) {
errno = EINVAL; /* Or whatever is appropriate */
return NULL;
}
p = malloc(/* whatever */);
/* Do stuff */
return p;
}
or
int my_function(sometype** pp) {
if (!pp) return -99;
/* Do something */
if (error_1_occurred) {
return -1;
} else if (error_2_occurred) {
return -2;
}
*pp = malloc(/* whatever */);
/* Do stuff */
return 0;
}
Using malloc
for this purpose is a very bad practice. Simply do:
static const char myerror;
return (void *)&myerror;
to get an "error value" pointer that will never compare equal to a pointer to any real object the rest of the program could obtain.
Edit: Originally I left out the &
. Fixed.
A NULL
pointer is nothing but a pointer to the memory location whit address 0, since memory is usually paged in 4k chunks, you can use the numbers from 1 to 4095 to return other kind of errors in your function because that chunk of memory (0 - 4096) won't be paged for your application (unless you explicitly map it using mmap
).
So it would be safe to do something like:
if(someError)
return NULL;
else if(someOtherError)
return (void *)1;
else
/* Do something else */