Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

free a cast pointer

Tags:

c

I want to know if it is ok to free() a pointer cast to another type. For instance if I do this:

char *p = malloc (sizeof (int));
int *q = (int *)p;
free (q);

I get no warning on gcc (-Wall).

On linux, the man pages on free says it is illegal to call free on a pointer that was not returned by malloc(), calloc() or realloc(). But what happens if the pointer was cast to another type in between?

I ask this because I read that the C standard does not require different pointer types (e.g. int* and char*) to have the same size, and I fail to understand how this is possible since they both need to be convertible to a void* in order to call the malloc/free functions.

Is the above code legal?

like image 961
Nicolas Grebille Avatar asked Dec 16 '22 18:12

Nicolas Grebille


2 Answers

It's probably safe, but it's not absolutely guaranteed to be safe.

On most modern systems, all pointers (at least all object pointers) have the same representation, and converting from one pointer type to another just reinterprets the bits that make up the representation. But the C standard doesn't guarantee this.

char *p = malloc (sizeof (int));

This gives you a char* pointer to sizeof (int) bytes of data (assuming malloc() succeeds.)

int *q = (int *)p;

This converts the char* pointer to an int* pointer. Since int is bigger than char, an int* pointer could require less information to indicate what it points to. For example, on a word-oriented machine, an int* might point just point to a word, while a char* has to contain a word pointer and an offset that indicates which byte within the word it points to. (I've actually worked on a system, the Cray T90, that worked like this.) So a conversion from char* to int* can actually lose information.

free (q);

Since free() takes an argument of type void*, the argument q is implicitly converted from int* to void*. There is no guarantee in the language standard that converting a char* pointer to int*, and then converting the result to void*, gives you the same result as converting a char* directly to a void*.

On the other hand, since malloc() always returns a pointer that's correctly aligned to point to any type, even on a system where int* and char* have different representations, it's unlikely to cause problems in this particular case.

So your code is practically certain to work correctly on any system you're likely to be using, and very very likely to work correctly even on exotic systems you've probably never seen.

Still, I advise writing code that you can easily demonstrate is correct, by saving the original pointer value (of type char*) and passing it to free(). If it takes several paragraphs of text to demonstrate that your code is almost certainly safe, simplifying your assumptions is likely to save you effort in the long run. If something else goes wrong in your program (trust me, something will), it's good to have one less possible source of error to worry about.

A bigger potential problem with your code is that you don't check whether malloc() succeeded. You don't do anything that would fail if it doesn't (both the conversion and the free() call are ok with null pointers), but if you refer to the memory you allocated you could be in trouble.

UPDATE:

You asked whether your code is legal; you didn't ask whether it's the best way to do what you're doing.

malloc() returns a void* result, which can be implicitly converted to any pointer-to-object type by an assignment. free() takes a void* argument; any pointer-to-object type argument that you pass to it will be implicitly converted to void*. This round-trip conversion (void* to something_else* to void*) is safe. Unless you're doing some kind of type-punning (interpreting the same chunk of data as two different types), there's no need for any casts.

Rather than:

char *p = malloc (sizeof (int));
int *q = (int *)p;
free (q);

you can just write:

int *p = malloc(sizeof *p);
...
free(p);

Note the use of sizeof *p in the argument to malloc(). This gives you the size of whatever p points to without having to refer to its type explicitly. It avoids the problem of accidentally using the wrong type:

double *oops = malloc(sizeof (int));

which the compiler likely won't warn you about.

like image 73
Keith Thompson Avatar answered Jan 04 '23 23:01

Keith Thompson


Yes, it's legal. free() takes a void pointer (void*), so the type doesn't matter. As long as the pointer passed to was returned by malloc/realloc/calloc it's valid.

like image 36
P.P Avatar answered Jan 04 '23 23:01

P.P