free a pointer and have it point to NULL

J

Johannes Bauer

Maybe.

After free(ptr), the value of ptr is *indeterminate*, meaning that any
attempt to refer to its value has undefined behavior.

Woo, really? I find that rather surprising. Does this mean that free()
specifically is a special case or was this just a writing error on your
part? If free behaved like all other functions, the value of ptr would
be well defined after the free (to be the same value as before the free,
since pointers are call-by-value).

Defererencing that pointer after a free has obviously indeterminate
consequences, but is also the pointer *value* indeterminate?

Regards,
Johannes

--
Zumindest nicht öffentlich!
Ah, der neueste und bis heute genialste Streich unsere großen
Kosmologen: Die Geheim-Vorhersage.
- Karl Kaos über Rüdiger Thomas in dsa <[email protected]>
 
B

Ben Bacarisse

Yes: "The value of a pointer becomes indeterminate when the object it
points to (or just past) reaches the end of its lifetime." (6.2.4 p2).

I think you are reading too much into the phrase. "Becomes
indeterminate" does not mean that any of the bits have to change. If
the standard said "becomes invalid" I don't think you'd worry about it.

A pointer bit pattern could, for example, become invalid as a result of
the free function fiddling with some memory management registers; any
reference to that value might then cause undefined behaviour (which,
remember, includes nothing bad happening). The standard has a term for
that sort of value: "indeterminate". It's unfortunate that "becomes
indeterminate" suggest that you can't know anything about the bits that
make up the value, but, in the C standard, the value of an object is an
interpretation of its bits, and that can change without the bits
themselves changing.
Of course it isnt. The pointer value cant be changed since the parameter
is a void * : VALUE. The free() function can not alter the value in the
pointer variable used to provide that void * value to the function.

The representation in the object does not need to change for it to
represent a trap representation -- a term is just standardese for a
value you can't use without undefined behaviour. When you unpack the
definitions of various the terms involved ("indeterminate value",
"unspecified value", "trap representation"), you end up with this
uncontroversial observation: before the call to free, the pointer is a
"valid value of the relevant type", but afterwards it "need not
represent a value of the object type".
 
S

Stefan Ram

Ben Bacarisse said:
Yes: "The value of a pointer becomes indeterminate when the object it
points to (or just past) reaches the end of its lifetime." (6.2.4 p2).

Sometimes, in C there is a irritating distinction some people make
between address values and pointers. Some people say that &x is an
address value, but a pointer is /an object/ storing an address value.

There is some support in N1570 itself for this:

»A pointer type describes an object whose value provides
a reference to an entity of the referenced type.«

It says »an object«, not »a value«!

I think the specification is misguided (written in an
inconsistend and too vague way) in this regard, but when
one takes this serious, we have this:

char * a; if( a = malloc( 1 ))
{ char * b = a;
free( a );
/* Now your quotation has /not/ said that b has become
indeterminate, because according to the pointer=object
point of view, b is a pointer different from a. */ }
 
S

Stefan Ram

/* Now your quotation has /not/ said that b has become
indeterminate, because according to the pointer=object
point of view, b is a pointer different from a. */ }

Sorry. Having re-read your quotation, I see that I erred in this regard.
 
K

Kaz Kylheku

Woo, really? I find that rather surprising. Does this mean that free()
specifically is a special case or was this just a writing error on your
part? If free behaved like all other functions, the value of ptr would
be well defined after the free (to be the same value as before the free,
since pointers are call-by-value).

A pointer which refers to any object object that has been destroyed
(a "dangling pointer") is indeterminate. This is not limited to the free
function: pointers to objects defined in automatic storage (block scope
non-static local variables) also become indeterminate when their enclosing
block terminates.

This can happen without a change in the value which is stored. The value
remains the same, but its meaning changes. The value is no longer on the
roster of valid pointers, and so it is possible to diagnose any use of that
value, even a harmless transmission from one variable to another without
dereferencing.

A tracing technique related to garbage collection could be used to find
all dangling pointers and overwrite them with a value like null or some other
value deemed useful for debugging. Because such pointers have indeterminate
values, implementors have this freedom.

By far the most common implementation is that nothing happens to such values at
all, and when the storage is re-used for a new object, dangling pointers appear
to be valid pointers referencing the new object.

The rule allows for sophisticated debugging tools which catch problems as early
as possible.

Usually, any use whatsoever of a dangling pointer indicates a bug; there is no
useful purpose behind it.

The exception are introspective programs designed to inspect the behaviors of
memory allocation: to answer questions like "is an object recycled
immediately?":

{
char *p = malloc(10), *q;
free(p);
q = malloc(10);

if (p == q) {
puts("ten byte object was recycled immediately");
}

free(q);
}

Or:

void *p, *q;

{
int x;
p = &x;
}

{
int y;
q = &y;
}

if (p == q) {
puts("Cool: x and y were placed in overlapping storage");
}

The rules in C are such that an implementation can diagnose these programs, and
even halt their execution.

(There is a workaround for that: to use memcmp to compare the pointers.
However, that raises the risk of the comparison being spoiled by
padding bits in a pointer which change its bitwise image, but not
it value (where it points)).
Defererencing that pointer after a free has obviously indeterminate
consequences, but is also the pointer *value* indeterminate?

It is the indeterminacy of the value which causes the behavior, upon
dereferencing, to be undefined.

The idea that the pointer value remains "good" from the point of view
of access, but cannot be dereferenced, would require a new category of value in
C, which isn't worth it: a category that isn't "indeterminate", but something
else.

There is a pointer value which has that behavior: it can be used, but not
dereferenced. Namely, the null pointer. However, a location which might hold a
null pointer can be accessed for a useful purpose: to test whether or not it is
null.
 
K

Kaz Kylheku

Sometimes, in C there is a irritating distinction some people make
between address values and pointers. Some people say that &x is an
address value, but a pointer is /an object/ storing an address value.

There is some support in N1570 itself for this:

»A pointer type describes an object whose value provides
a reference to an entity of the referenced type.«

(But does not provide pass-by-reference, haha.)
It says »an object«, not »a value«!

It says "object, whose value ..."!

C types describe objects: i.e. they specify an in-memory representation
that can be broken down into bits and bytes (the quantity of the latter being
the "sizeof" that type).

C values are manipulated in the data processor such that they are not always
objects, but their type specifies details about what they look like when they
are stored.

The above sentence is primarily concerned with defining "pointer type".
 
J

James Kuyper

Woo, really? I find that rather surprising. Does this mean that free()
specifically is a special case or was this just a writing error on your
part? If free behaved like all other functions, the value of ptr would
be well defined after the free (to be the same value as before the free,
since pointers are call-by-value).

Defererencing that pointer after a free has obviously indeterminate
consequences, but is also the pointer *value* indeterminate?

Yes.
"... The value of a pointer becomes indeterminate when the object it
points to (or just past) reaches the end of its lifetime." (6.2.4p2)
"The lifetime of an allocated object extends from the allocation
until the deallocation." (7.22.3p1)
"The free function causes the space pointed to by ptr to be deallocated
...." (7.22.3.3p2)

Keep in mind that it is possible for the value of a pointer to become
indeterminate without any change to it's bit patterns. There are real
systems which have the ability to make blocks of memory switch from
being valid to being invalid while a program is running, and because of
the clauses listed above, a fully conforming implementation of free()
could have that effect.

I personally believe that such changes are the only permitted way for
free() to render a pointer invalid - that it is not permitted to modify
the bit pattern of the pointer itself. However, I've had extended
discussions about that on this newsgroup with people who felt otherwise,
with neither side ever accepting the validity of the other's arguments.

Keep in mind that all pointers anywhere in the program that point at any
part of an allocated block of memory have an indeterminate value after
that block is deallocated. Nothing remotely resembling the normal
semantics for C function calls would allow all of those pointers to have
their bit patterns modified just by calling free() on one of those pointers.
 
K

Keith Thompson

James Kuyper said:
Keep in mind that it is possible for the value of a pointer to become
indeterminate without any change to it's bit patterns. There are real
systems which have the ability to make blocks of memory switch from
being valid to being invalid while a program is running, and because of
the clauses listed above, a fully conforming implementation of free()
could have that effect.

I personally believe that such changes are the only permitted way for
free() to render a pointer invalid - that it is not permitted to modify
the bit pattern of the pointer itself. However, I've had extended
discussions about that on this newsgroup with people who felt otherwise,
with neither side ever accepting the validity of the other's arguments.

Keep in mind that all pointers anywhere in the program that point at any
part of an allocated block of memory have an indeterminate value after
that block is deallocated. Nothing remotely resembling the normal
semantics for C function calls would allow all of those pointers to have
their bit patterns modified just by calling free() on one of those pointers.

A compiler could treat the free() function specially, but it's unlikely
that

char *p = malloc(10);
if (p ! NULL) {
p ++;
free(p-1);
}

would modify the representation if p.

Furthermore, the bytes that make up the representation of p are
themselves objects which "retain their last-stored values between
program startup and program termination".

On the other hand, I seem to recall that there's a DR that says free()
*can* change the representation of a pointer object after its value is
passed to free(). I'll try to track it down later.
 
G

glen herrmannsfeldt

Johannes Bauer said:
On 22.04.2014 23:37, Keith Thompson wrote:
(snip)
Woo, really? I find that rather surprising. Does this mean that free()
specifically is a special case or was this just a writing error on your
part? If free behaved like all other functions, the value of ptr would
be well defined after the free (to be the same value as before the free,
since pointers are call-by-value).
Defererencing that pointer after a free has obviously indeterminate
consequences, but is also the pointer *value* indeterminate?

For protected mode x86 (80286 and up) loading a segment selector
for a non-allocated segment will fail. That is, a segmentation
violation. (You always wondered where the name came from?)

Now, most compilers won't actually load one into a segment
register when not needed to actually reference data, but the
standard allows it to fail.

(Selector zero is reserved as the null segment, so there is some
value to use when there is nothing else to load.)

-- glen
 
J

James Kuyper

On 04/29/2014 09:59 AM, James Kuyper wrote:
....
... There are real
systems which have the ability to make blocks of memory switch from
being valid to being invalid while a program is running, ...

I left out a key step: on such systems it is often the case that loading
an invalid into an address register is sufficient to cause your program
abort. Dereferencing the pointer isn't necessary. This is a safety
measure: the designers felt that any program written badly enough to
treat an invalid address as if were a valid one, is sufficiently
dangerous that it's safest to have a policy of stopping such programs as
soon as possible.
 
K

Keith Thompson

Keith Thompson said:
A compiler could treat the free() function specially, but it's unlikely
that

char *p = malloc(10);
if (p ! NULL) {
p ++;
free(p-1);
}

would modify the representation if p.

Furthermore, the bytes that make up the representation of p are
themselves objects which "retain their last-stored values between
program startup and program termination".

On the other hand, I seem to recall that there's a DR that says free()
*can* change the representation of a pointer object after its value is
passed to free(). I'll try to track it down later.

It's C99 DR #260, submitted by Clive D.W. Feather in 2004.
http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_260.htm

As far as I can tell, the DR didn't lead to any specific wording in the
C11 standard, but the committee response was:

Question 1:
Values may have any bit-pattern that validly represents them
and the implementation is free to move between alternate
representations (for example, it may normalize pointers,
floating-point representations etc.). In the case of an
indeterminate value all bit-patterns are valid representations
and the actual bit-pattern may change without direct action of
the program.

Question 2:
If two objects have identical bit-pattern representations and
their types are the same they may still compare as unequal
(for example if one object has an indeterminate value) and if
one is an indeterminate value attempting to read such an object
invokes undefined behavior. Implementations are permitted to
track the origins of a bit-pattern and treat those representing
an indeterminate value as distinct from those representing a
determined value. They may also treat pointers based on different
origins as distinct even though they are bitwise identical.

In reference to "Question 1", I agree that it makes sense to permit an
implementation to normalize pointers and floating-point objects when two
or more representations have the same value. I wrote above that the
bytes making up the representation of an object are themselves objects,
which are required by 6.2.4p2 to "retain[] its last-stored value
throughout its lifetime".

I'm no longer quite as sure of that, given the way 6.2.6.1 defines
*object representation*:

Values stored in non-bit-field objects of any other object type
consist of n × CHAR_BIT bits, where n is the size of an object
of that type, in bytes. The value may be copied into an object
of type unsigned char [n] (e.g., by memcpy); the resulting set
of bytes is called the *object representation* of the value.

which talks about the bytes *after copying them from the object*,
so at least that paragraph doesn't imply that the bytes within the
object itself are objects.

On the other hand, an object is defined as a "region of data storage in
the execution environment, the contents of which can represent values";
a byte within a pointer object, for example, would seem to qualify.

On the other other hand, a program that depends on a byte within a freed
pointer retaining its previous value would be perverse, and I don't feel
*too* bad about the possibility of breaking it.
 
J

James Kuyper

On 04/29/2014 02:50 PM, Keith Thompson wrote:
....
or more representations have the same value. I wrote above that the
bytes making up the representation of an object are themselves objects,
which are required by 6.2.4p2 to "retain[] its last-stored value
throughout its lifetime".

I'm no longer quite as sure of that, given the way 6.2.6.1 defines
*object representation*:

Values stored in non-bit-field objects of any other object type
consist of n × CHAR_BIT bits, where n is the size of an object
of that type, in bytes. The value may be copied into an object
of type unsigned char [n] (e.g., by memcpy); the resulting set
of bytes is called the *object representation* of the value.

which talks about the bytes *after copying them from the object*,
so at least that paragraph doesn't imply that the bytes within the
object itself are objects.

On the other hand, an object is defined as a "region of data storage in
the execution environment, the contents of which can represent values";
a byte within a pointer object, for example, would seem to qualify.

On the other other hand, a program that depends on a byte within a freed
pointer retaining its previous value would be perverse, and I don't feel
*too* bad about the possibility of breaking it.

At that level, I would agree. However, if 6.2.4p2 is not to be
interpreted as prohibiting such changes, it renders essentially all uses
of memcmp() useless, at least for any type that is allowed to have
multiple representations of the same value (which could includes any
integer type with padding bits, any signed type with anything other 2's
complement representation, and all floating point and pointer types).
 
G

glen herrmannsfeldt

Robert Wessel said:
On Tue, 29 Apr 2014 12:41:07 -0400, James Kuyper
(snip)

More specifically, the way x86 processors starting with the 80286
work, is that when you load a segment selector into a segment
register, the processor also loads a segment descriptor from the
appropriate entry in a descriptor table. If the descriptor is
invalid, an interrupt occurs.
While not specifically a C example, we supported the Realia Cobol
compiler in the 16-bit era, and on protected mode systems, you'd have
exactly that problem.

C compilers I know tend to avoid loading segment registers until
they are pretty much ready to use them. For example, pointer
assignment and pointer comparison are not done using a segment
register. (I don't believe that there are compare instructions
for segment registers. Assignment could be done through load
and store, though.)
Cobol is mostly pass-by-reference (in those days it was all
pass-by-ref, there is some pass-by-value in the modern standards), and
the Realia calling convention put the (pointers to the) first two
parameters in DS:SI and ES:DI. Merely calling a subroutine with a
pointer to storage that had been returned to the OS would cause an
abend when the caller tried to load the appropriate segment register.

There have been many systems where you could load from any memory
address, such as real mode MS-DOS. Some programmers for those
systems tend to ignore fetch from bad addresses, such as past the
end of arrays. (In most cases, just after the end of an array is
still part of your address space.)

Much code for languages that don't require short-circuit IF
evaluation works because the out of bounds fetch doesn't cause
any problems.

And, by the way, all the segment selector logic is still in
current, and likely future, x86 processors. It isn't some strange
system that died out years ago, now only found in a museum.
(But there are some of those, too.)

-- glen
 
M

Michael Angelo Ravera

memory allocated on the heap once freed should the pointer be assigned NULL?

There is a memory allocations scheme that some of my collegues invented back in the 1980s that was then known as "Handles" which used double pointers.It allowed for memory to be moved behind the scenes (back when HARD DRIVESwould only hold 5MB). Since you could make copies of the Handle, it was possible to set the actual address to zero (or some other illegal value) whenthe memory ultimately represented by the Handle was freed. Of course, if you took a shortcut and actually wrote down a reference to the memory yourself, problems could ensue (but the memory could have been moved behind the scenes anyway, so most people didn't).

However, I routinely set pointers to an illegal value when I want to know whether something has been allocated and then deallocated. I often have macros to allocate and deallocate.

#define gimme(p,t,size) {if ((!p) && (!(p = (t) malloc (size)))) dealwithit();}
#define lose(p) {if (p) free (p); p = 0;}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,573
Members
45,046
Latest member
Gavizuho

Latest Threads

Top