4

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?

Axalo
  • 2,953
  • 4
  • 25
  • 39
Nicolas Grebille
  • 1,332
  • 8
  • 15

4 Answers4

6

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.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • Thanks for your reply (I don't check malloc() return value only because I wanted a small example). Suppose I have a void* pointer that I allocate "as" an int* or double* (and I always cast it to the correct type before actually dereferencing it), I can free it as a void* pointer when I'm done, without having to cast it to its actual type? – Nicolas Grebille Oct 02 '12 at 23:04
  • @NicolasGrebille: This: `double *ptr = malloc(sizeof *ptr); ... free(ptr);` is safe, and is the best way to do this. Unless you're doing something very strange, you should never need to cast pointers used with `malloc()` and `free()`; the *implicit* conversions will do exactly what you need. – Keith Thompson Oct 02 '12 at 23:07
  • Yes, but if I have a void* pointer in a struct that could be allocated "as" a double* or int*, I need to do the casts myself. – Nicolas Grebille Oct 02 '12 at 23:09
  • @NicolasGrebille: Yes, there are cases where you actually declare an object (a struct member, for example) as `void*` and can use it to point to different types. But conversions between `void*` and other pointer types don't require casts; just assign the value and the conversion is implicit. – Keith Thompson Oct 02 '12 at 23:14
  • So if I understand you correctly, a cast of any pointer type to/from void* is guaranteed not to loose information, but this is not the case with other pointer types? I mean, with your Cray T90 example, it is not the same to cast from a char* to an int*, and to cast from a char* to a void* to an int*? – Nicolas Grebille Oct 02 '12 at 23:26
3

Yes the pointer is not changed, the cast is merely how the compiler interprets the bunch of bits.

edit: The malloc call returns an address in memory ie a 32(or 64) bit number. The cast only tells the compiler how to interpret the value stored at that address, is it a float, integer, string etc, and when you do arithmatic on the address how big a unit should it step in.

Martin Beckett
  • 94,801
  • 28
  • 188
  • 263
  • 2
    Are you sure? Is there any guarantee that `(void*)((int*)p)` is the same as `(void*)p` for any `p`? As far as I know, the only guarantee is that you can convert a `T *` to a `void *` and back and get the same pointer, but that isn't saying anything about the *value* of the other side. – Kerrek SB Oct 02 '12 at 22:46
  • @KerrekSB why wouldn't it be different? We're not even doing pointer arithmetic here. –  Oct 02 '12 at 22:47
  • @H2CO3: Why wouldn't it be different? That's exactly my question. – Kerrek SB Oct 02 '12 at 22:51
  • You mean that as long as I don't dereference or increment the pointer, the cast could be compiled to a no-op? What I don't understand is how it could be legal to a void* and not to any other pointer type. If I cast a int* to a void*, then to a char* and then to a void*, and then to an int* back, is all of this legal? I don't understand how this could be possible if all pointer types are not the same in the implementation – Nicolas Grebille Oct 02 '12 at 22:55
  • The malloc gives an address in memory, ie a 32bit number. You can tell the compiler that the value stored there is a float/int/string etc. But it doesn't change the address - just how arithmatic on the address works – Martin Beckett Oct 02 '12 at 23:05
  • 1
    Yes, that's what I thought, but I learnt here that this is not always true: http://stackoverflow.com/questions/1241205/are-all-data-pointers-of-the-same-size-in-one-platform. So what if char* and int* have not the same representation? – Nicolas Grebille Oct 02 '12 at 23:51
3

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.

P.P
  • 117,907
  • 20
  • 175
  • 238
0

The code is legal, however it is not necessary. Since pointers only point to the address where data is stored, there is no need to allocate space, or subsequently free it.

blearn
  • 1,198
  • 10
  • 17