Home > Software engineering >  Do a pointer after free and a null pointer not compare equal?
Do a pointer after free and a null pointer not compare equal?

Time:07-08

I would like to know whether the C standard defines the behaviour of the following program or the behaviour is undefined:

int main() {
    int* p = (int*) malloc(40);
    if (p==NULL)
        ;
    else {
        free(p);
        int* q = (int*) malloc(40);
        if (p==NULL)
            printf("null");
        else
            printf("not null");
    }
    return 0;
}

The C standard says:

3.15 object region of data storage in the execution environment, the contents of which can represent values"

6.5.9 If one operand is a pointer and the other is a null pointer constant, the null pointer constant is converted to the type of the pointer. If one operand is a pointer to an object type and the other is a pointer to a qualified or unqualified version of void, the former is converted to the type of the latter.

Two pointers compare equal if and only if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function, both are pointers to) one past the last element of the same array object, or one is a pointer to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space.

7.22.3.3 The free function The free function causes the space pointed to by ptr to be deallocated, that is, made available for further allocation. If ptr is a null pointer, no action occurs. Otherwise, if the argument does not match a pointer earlier returned by a memory management function, or if the space has been deallocated by a call to free or realloc, the behavior is undefined.

It seems to me that after free(p) we cannot assume anything about *p but still we can assume that (p==NULL) would return false. Am I right?

CodePudding user response:

still we can assume that (p==NULL) would return false

No. C 2018 6.2.4 2 says “The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.” Indeterminate means an unspecified value or a trap representation (3.19.2), and unspecified value means “valid value of the relevant type where this document imposes no requirements on which value is chosen in any instance” (3.19.3). This means that after free(p), the program may behave as if p is null at one moment and non-null at another.

This rule originated because some C implementations used additional information besides the pointer itself to manage memory. Accessing the memory referred to by a pointer required looking some things up in data structures associated with the pointer. When the memory is freed, those data structures could be altered and rendered invalid. Then merely using the pointer for its value could cause problems, as interpreting the value of the pointer required using data that might have been changed.

That is largely archaic in modern C implementations, but the rule persists, and it is now built into compiler optimization. When the optimizer sees p==NULL after free(p), it may treat the code as if p is null even though free(p) could not have changed the actual bytes representing p, and therefore it is allowed to treat this code:

       if (p==NULL)
            printf("null");
        else
            printf("not null");

as if it were:

        printf("null");

CodePudding user response:

In order for a function to be able to change the value of a variable, you need to provide the address of the variable to the function. free doesn't have that and can therefore not change it. Compare with:

void myfree(void **pp) {
    free(*p);
    *p = NULL;
}

This function, when called with free((void*)&p);, would on the other hand both free and change the value so the value of p would be NULL afterwards.

#include <stdio.h>
#include <stdlib.h>

void myfree(void** p) {
    free(*p);
    *p = NULL;
}

int main() {
    int* p = (int*)malloc(40);
    if (p == NULL) return 1;
    myfree((void*)&p);
    printf("%p\n", (void*)p);   // prints something indicating that it's NULL
}

Also note that your program will most likely leak 40 bytes.

CodePudding user response:

free(NULL) is a valid operation and thus calling free(p) when p = NULL before the function call could lead to p == NULL returning true afterwards.

  • Related