Trevor L. Jackson said:
Keith Thompson wrote: [...]
Even if printf("%p", ...) were defined to accept a stale pointer,
there would still be no way to pass one to it without invoking
undefined behavior just by evaluating it before the call.
Hmmm. Clearly assigning a stale pointer to another variable would be
unacceptable. But your statement implies that:
(void)stale_ptr;
would also be unacceptable. Is that implication accurate?
I believe so. The value of stale_ptr is indeterminate (C99 6.2.4p2),
meaning that it's either an unspecified value or a trap representation
(3.17.2). If it's a trap representation, reading it by an lvalue
expression that doesn't have character type invokes undefined behavior
(6.2.6.1p5).
Realistically, of course, any decent compiler will generate no code for
(void)stale_ptr;
and the program will not trap, but that's just another possible
consequence of undefined behavior.
Digression follows.
I've been wondering why the standard says that the value of a free()d
pointer becomes indeterminate and not that it becomes a trap
representation; since evaluating it invokes undefined behavior anyway,
calling it a trap representation would be less ambiguous and no
strictly conforming program could tell the difference. But I think
I've just figured out why it's worded this way. There actually are
circumstances in which a free()d pointer can be evaluated without
invoking undefined behavior. It's sometimes possible (but not
reliably) to determine that the value of a stale pointer happens not
to be a trap representation. Here's a program that demonstrates this:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
int main(void)
{
int *ptr1, *ptr2;
ptr1 = malloc(sizeof *ptr1);
assert(ptr1 != NULL);
free(ptr1);
/*
* The value of ptr1 is now indeterminate.
* It could be either a trap representation
* or an unspecified value.
*/
ptr2 = malloc(sizeof *ptr2);
assert(ptr2 != NULL);
/*
* ptr2 has a valid value; it points to an object.
*/
printf("ptr2 = %p\n", (void*)ptr2);
/*
* Is the value of ptr1 a trap representation,
* or is it merely unspecified?
*/
if (memcmp(ptr1, ptr2, sizeof(int*)) == 0) {
/*
* The value of ptr1 is unspecified, and happens
* to be the same as the value of ptr2 (malloc()
* re-used the memory that was just free()d). We
* know ptr2 doesn't have a trap representation,
* so we can infer that ptr1 doesn't have a trap
* representation.
*/
printf("ptr1 = %p\n", (void*)ptr1);
}
else {
/*
* ptr1 may or may not have a trap
* representation; we can't reliably tell.
*/
printf("?\n");
}
return 0;
}
This is not a useful program other than as an experiment, and it's not
strictly conforming because it depends on unspecified behavior, but
it's portable and it can *sometimes* print the value of ptr1 after
it's been free()d without invoking undefined behavior. On one
implementation, the output is:
ptr2 = 0xa050248
ptr1 = 0xa050248
If the standard specified that a free()d pointer acquires a trap
representation, this program would invoke undefined behavior on
evaluating ptr1 -- and since ptr2 has the same value and
representation as ptr1, any malloc/free implementation that re-uses
addresses might impose undefined behavior on programs that use it.