Does freeing an int* which was assigned to a char*

2020-04-04 16:44发布

问题:

The title maybe confusing. Suppose str is a pointer allocated by malloc. ptr, of type int*, is assigned to it and is freed as shown by the code snippet below:

char* str = malloc(64);
int* ptr = str;

free(ptr);

I've tried to compile the above code. It just gives a warning:

source_file.c: In function ‘main’:
source_file.c:10:16: warning: initialization from incompatible pointer type
     int* ptr = str;
                ^

Does the above code invoke Undefined Behavior?
Does the above code snippet free the memory allocated by malloc for str?

回答1:

Does the above code invoke Undefined Behavior?

It depends.

From C11 draft 6.3.2.3/7:

A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned) for the referenced type, the behavior is undefined.

As the alignment for a char might be different from an int, that is probably less restrictive, assigning a char * pc to an int * pi might lead to pi being misaligned.

However for the specific example given by the OP:

char * pc = malloc(64);
int * pi = pc;

the behaviour would be defined, as (See Alter Mann's comment) malloc() is guaranteed to return a block of memory properly aligned.

From C11 draft 7.22.3:

The pointer returned [by aligned_alloc, calloc, malloc, and realloc] if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement ...

An example which would lead to undefined behaviour, due to misalignment, is:

char * pc = malloc(64);
int * pi = pc + 1;

Does the above code snippet free the memory allocated by malloc for str?

In case the former assignment would have introduced undefined behaviour this question is irrelevant, as anything could happen with UB having been invoked already.

If else the prior assignment wouldn't have invoked UB, the call to free() would perfectly de-allocate the block of memory referenced, as converting back the pointer value from int * to void *, as originally provided by malloc(), is well defined.

From C11 draft 6.3.2.3/7 (cont/):

Otherwise, when converted back again, the result shall compare equal to the original pointer

and

From C11 draft 6.3.2.3/1:

A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer



回答2:

No. It doesn't invoke undefined behaviour. The warning is simply about incompatible types which you could cast.

char* str = malloc(64);
int* ptr = (int*) str;
free(ptr);

free does take a void pointer and the above has no problems. However, using the result of such a value may invoke undefined behaviour due to alignment of int type and char type. As such the conversion of char* to int* itself doesn't lead to undefined.



回答3:

Does the above code invoke Undefined Behavior?

No.

Does the above code snippet free the memory allocated by malloc for str?

Yes.

Just for clarification, some notes on UB regarding dynamic allocation:

Memory returned by malloc is aligned to take any possible value. Such memory has no declared type and its effective type is set through storage.

If you do

*ptr = 42;

the first sizeof (int) bytes of the memory block will now be of type int and may only be read as such, ie

float val = *(float *)ptr;

will be UB.

However,

*(float *)ptr = 42.0;

would be legal as it re-sets the effective type, now in turn making reads through *ptr invalid.

In addition, it is always legal to access any object through pointers of type char or unsigned char.



回答4:

It may invoke UB, based on endianness, alignments or such int vs char typecasting when accessed. When you do malloc all it does is return a void* which can be of any data type(and in some cases may require typecasting). It doesn't make a difference if you put a pointer in char* to int*, but there would be difference in access units, i.e in case of ints 4 bytes at a time vs in char 1 byte at a time. So, that code in your question wouldn't invoke UB, but memory accesses might.

As for second question, yes calling free on ptr will cause memory pointed by str also to be freed. Now, str would be a dangling pointer.