why doesn't the following program crash.
int main ()
{
char *p;
int x;
p = (char*) malloc(10);
x = (int) p;
free (p);
p = (char *) x;
*p = 1;
}
No particular reason. It could crash on some systems; the fact that
it doesn't do so on yours is just bad luck.
The program exhibits multiple instances of undefined behavior:
It calls malloc with no visible declaration, so the compiler quietly
assumes that it takes an argument of type int and returns a result of
type int (in fact it takes an argument of type size_t and returns a
result of type void*). The bogus int result is then converted to
char*. (Solution: add "#include <stdlib.h>".)
It converts a value from char* to int. This might work on some
systems; on others, it might lose information (say, if int is 32 bits
and char* is 64 bits) or worse.
It converts a value from int to char*. Again, this might work on some
systems, but on others it might not.
It dereferences the resulting pointer and stores an int value in the
location to which it points. If information was lost in the
conversion from char* to int and back to char*, this could attempt to
store the int value 1 at some arbitrary location, with arbitrarily bad
results. Just dereferencing the pointer, if only to examine the
target value without storing anything, would invoke undefined
behavior. For that matter, just examining the pointer value without
dereferencing it could invoke undefined behavior.
Here's a version of your program that avoids *most* of these problems:
#include <stdlib.h>
int main(void)
{
char *p;
p = malloc(10);
if (p == NULL) {
return EXIT_FAILURE;
}
free(p);
*p = 1;
return 0;
}
I think you were converting the pointer value to int and saving it in
a separate object in attempt to "hide" it from the compiler. This
isn't really necessary to illustrate the point; the code above does
the same thing.
Assuming malloc succeeds, p points to the first byte of a newly
allocated 10-byte object. Calling free() causes that object to reach
the end of its lifetime, and invalidates any pointer that points to
it. Note that p is passed to the free() *by value*, as all function
arguments are, so it still contains the same bit pattern that it did
after the successful call to malloc() -- but that bit pattern, rather
than representing a valid pointer value, now represents an invalid
pointer value. The value of p is now indeterminate, and any attempt
to dereference, or even to examine its value, invokes undefined
behavior.
But undefined behavior doesn't necessary cause your program to crash.
In a typical implementation, free() does some bookkeeping that allows
that 10-byte chunk of memory to be available for later allocation --
but the memory is still there, in the same place. You just no longer
"own" it. You can try to modify it, and it's quite likely that you'll
succeed; the C implementation isn't required to do anything to stop
you. It's entirely *your* responsibility to refrain from touching
memory you don't own.
Some languages require run-time checking to catch this kind of error.
C allows compilers to provide such checking, but doesn't require it,
and most C compilers don't provide it. The tendency is to assume that
the programmer knows what he's doing.