C
Chris Torek
True enough; but there must be a function version as well:
void (*fp)(void *);
...
fp = input_value == 42 ? free : zorg;
fp(ptr);
In the end, it is "too difficult" (and pointless, if you will pardon
the wording ) for free() to change the bits stored in an object
passed to it.
Here is the real problem -- the disconnect: the *bit pattern* and
the *value* are not the same thing!
Those who have written assembly code, or investigated thier C
implementations far enough, are already familiar with this with
respect to floating-point numbers. Typically "int" or "long"
is 4 or 8 bytes, and "float" or "double" is also 4 or 8 bytes
-- but a 4-byte "int" that represents (say) 12345 is *not*
also 12345.0f, not at all.
C99 makes it clear that there is a "representation" (as stored
in an object) for some particular value. The representation is
the bit-pattern in memory, and using "unsigned char *" pointers,
you can always inspect the representation of any addressable
object:
#include <stdio.h>
void show(const char *, void *, size_t);
int main(void) {
int i = 12345;
float f = 12345.0;
show("int i = 12345 ", &i, sizeof i);
show("float f = 12345.0", &f, sizeof f);
return 0;
}
void show(const char *prefix, void *mem, size_t len) {
size_t i;
unsigned char *cp = mem;
printf("%s:", prefix);
for (i = 0; i < len; i++)
printf(" %2.2x", (unsigned int)cp);
printf("\n");
}
% cc -o t -O -ansi -pedantic -W -Wall t.c
% ./t
int i = 12345 : 39 30 00 00
float f = 12345.0: 00 e4 40 46
%
Now, the trick to free() is that, while it is virtually certain
not to change the bit pattern -- the *representation* of some value
-- what it *can* do is change the *meaning* of those bits. Just
as (in this case) the bits "39 30 00 00" "mean" 12345 when they
represent an int, we need a completely different set of bytes to
"mean" 12345.0 as a float. The free() function can, in effect,
change the way that a pointer is interpreted.
Imagine a system just like your typical 32-bit Intel, except that
pointers use *eight* bytes. Half the pointer is used as an index
into a table. When table is set to 0, the pointer is considered
invalid, but when table is set nonzero, the pointer is valid.
The free() function clears table -- so now, even though the
"memory address" half of the pointer is still good, the pointer
itself is invalid.
The pointer is unchanged, but it has gone from "valid" to "invalid".
The *representation* is unchanged, but the *value* is different.
void (*fp)(void *);
...
fp = input_value == 42 ? free : zorg;
fp(ptr);
In the end, it is "too difficult" (and pointless, if you will pardon
the wording ) for free() to change the bits stored in an object
passed to it.
If anybody looks at the standard to learn how to call free(),
they *should* be able to count on the value (i.e. the bit pattern)
of "ptr" not changing. (Why they would want to, I can't say).
Here is the real problem -- the disconnect: the *bit pattern* and
the *value* are not the same thing!
Those who have written assembly code, or investigated thier C
implementations far enough, are already familiar with this with
respect to floating-point numbers. Typically "int" or "long"
is 4 or 8 bytes, and "float" or "double" is also 4 or 8 bytes
-- but a 4-byte "int" that represents (say) 12345 is *not*
also 12345.0f, not at all.
C99 makes it clear that there is a "representation" (as stored
in an object) for some particular value. The representation is
the bit-pattern in memory, and using "unsigned char *" pointers,
you can always inspect the representation of any addressable
object:
#include <stdio.h>
void show(const char *, void *, size_t);
int main(void) {
int i = 12345;
float f = 12345.0;
show("int i = 12345 ", &i, sizeof i);
show("float f = 12345.0", &f, sizeof f);
return 0;
}
void show(const char *prefix, void *mem, size_t len) {
size_t i;
unsigned char *cp = mem;
printf("%s:", prefix);
for (i = 0; i < len; i++)
printf(" %2.2x", (unsigned int)cp);
printf("\n");
}
% cc -o t -O -ansi -pedantic -W -Wall t.c
% ./t
int i = 12345 : 39 30 00 00
float f = 12345.0: 00 e4 40 46
%
Now, the trick to free() is that, while it is virtually certain
not to change the bit pattern -- the *representation* of some value
-- what it *can* do is change the *meaning* of those bits. Just
as (in this case) the bits "39 30 00 00" "mean" 12345 when they
represent an int, we need a completely different set of bytes to
"mean" 12345.0 as a float. The free() function can, in effect,
change the way that a pointer is interpreted.
Imagine a system just like your typical 32-bit Intel, except that
pointers use *eight* bytes. Half the pointer is used as an index
into a table. When table is set to 0, the pointer is considered
invalid, but when table is set nonzero, the pointer is valid.
The free() function clears table -- so now, even though the
"memory address" half of the pointer is still good, the pointer
itself is invalid.
The pointer is unchanged, but it has gone from "valid" to "invalid".
The *representation* is unchanged, but the *value* is different.