how to release memory?

P

pereges

I usually initialize all my data structure pointers to null. Then if
an error occurs I first print an error message, and then call a
killall function that releases the memory. eg :

typedef struct mesh
{
int ntri, int nvert;
triangle *tri; /* triangle array */
vector *vert; /* vertex array */

}mesh;

mesh *m_ptr;

void killall()
{
killmesh();
...
...
}

void killmesh(void)
{
if (m_ptr != NULL)
{
if (m_ptr->vert != NULL)
{
free(m_ptr->vert);
}

if (m_ptr->tri != NULL)
{
free(m_ptr->tri);
}

free(m_ptr);
}
}

If the value of the pointer after freeing the memory block pointed by
it is indeterminate, then I might as well make m_ptr->vert , m_ptr-
 
B

Barry Schwarz

Examining the value of a pointer (p == NULL) is defined as 0 or 1
depending on the value of the pointer. If 1 then p is a valid pointer to
NULL. Thou shalt not dereference it.

Not necessarily true. If p is indeterminate, the act of evaluating it
invokes undefined behavior which may manifest itself as letting the
expression evaluate to 1.
There is no way to know whether a non-NULL pointer is valid or
indeterminate by examining it. Clearly, 'free(p);' will cause the value
of p to be indeterminate. You can examine p until you're blue but
nothing about it will tell you it has been freed.

As JW noted, once the value is indeterminate you may no longer attempt
to evaluate it at all.


Remove del for email
 
B

Barry Schwarz

I usually initialize all my data structure pointers to null. Then if
an error occurs I first print an error message, and then call a
killall function that releases the memory. eg :

typedef struct mesh
{
int ntri, int nvert;
triangle *tri; /* triangle array */
vector *vert; /* vertex array */

}mesh;

mesh *m_ptr;

void killall()
{
killmesh();

Since you potentially have more than one pointer (your first
sentence), wouldn't it be better to pass the particular pointer of
interest to killmesh.
...
...
}

void killmesh(void)
{
if (m_ptr != NULL)
{
if (m_ptr->vert != NULL)
{
free(m_ptr->vert);
}

if (m_ptr->tri != NULL)
{
free(m_ptr->tri);
}

free(m_ptr);
}
}

If the value of the pointer after freeing the memory block pointed by
it is indeterminate, then I might as well make m_ptr->vert , m_ptr-

There is no if; the standard mandates this.

It won't hurt but ->ver and ->tri are about to go out of existence
when m_ptr is freed so for those two it doesn't help much either.


Remove del for email
 
P

pereges

Since you potentially have more than one pointer (your first
sentence), wouldn't it be better to pass the particular pointer of
interest to killmesh.

What if I make the pointer m_ptr is an extern ? In that case, passing
the pointer is optional.


There is no if; the standard mandates this.
It won't hurt but ->ver and ->tri are about to go out of existence
when m_ptr is freed so for those two it doesn't help much either.



ok, thanks for the tip.
 
K

Keith Thompson

pete said:
pete said:
Joe Wright wrote: [...]
No. Let me ask you..

char a[] = "Hello World";
char *s, *t;
s = malloc(strlen(a) + 1); /* assume success */
strcpy(s, a);
t = s + 6;

Now *s is 'H' and *t is 'W' and (t - s) yields 6.

free(s);

Now the values of s and t are indeterminate because the objects to
which they pointed are now unallocated.

But (t - s) still yields 6 does it not?
No.
Is this Undefined Behavior?
Yes.
At that point, even this statement:
s;
contains undefined behavior.

According to my interpretation
of Mr. Feather's interpretation
of the part of the standard that he wrote,
I went too far with that last one, since
s;
means the same thing as
(void)s;

Yes, ``s;'' means the same thing as ``(void)s;'' -- but the behavior
is undefined in both cases. Clive's quoted statement:

refers specifically to a call to memcpy().
 
K

Keith Thompson

pete said:
It's case 4, from Clive's other quoted statement,
which came out more messy than I remember sending it
(I miss my old newsreader):

(1) value is read, such as "pp = p" or "(void *) p";
(2) value is written, such as "p = NULL";
(3) value is read and written, such as "p += 1";
(4) value is ignored, such as "(void) p" or "sizeof p".

Cases (1) and (3) are undefined behaviour.
Cases (2) and (4) are not.

I agree on cases 1, 2, and 3, but not 4. The standard specifically
says that p is not evaluated in "sizeof p" (if p isn't a VLA). I see
no such statement for "(void) p".

In fact, see C99 6.3.2.2:

The (nonexistent) value of a void expression (an expression that
has type void) shall not be used in any way, and implicit or
explicit conversions (except to void) shall not be applied to such
an expression. If an expression of any other type is evaluated as
a void expression, its value or designator is discarded. (A void
expression is evaluated for its side effects.)

C99 6.5.4, Cast operators, also says nothing about not evaluating the
operand of a void cast.

It seems clear that p *is* evaluated, and if its value is
indeterminate the behavior is undefined (though it's very likely that
a compiler will not generate code to evaluate it if there are clearly
no side effects). I think Clive got this one wrong.
 
B

Barry Schwarz

snip
It's case 4, from Clive's other quoted statement,
which came out more messy than I remember sending it
(I miss my old newsreader):

We've lost the part you refer to but I believe the only thing of
interest is that a pointer p is indeterminate.
(1) value is read, such as "pp = p" or "(void *) p";
(2) value is written, such as "p = NULL";
(3) value is read and written, such as "p += 1";
(4) value is ignored, such as "(void) p" or "sizeof p".

Cases (1) and (3) are undefined behaviour.

Yes because p is evaluated which is the trigger for the undefined
behavior.
Cases (2) and (4) are not.

Case 2 is not undefined simply because p is not evaluated.

Case 4 represents two different situations:

(void)p still causes p to be evaluated, even if the result of the
evaluation is discarded. This invokes undefined behavior. I'm not
sure but I expect that if the compiler optimizes the statement away
**completely**, then the undefined behavior caused by the evaluation
also disappears.

sizeof p will not cause undefined behavior because sizeof will
not evaluate p.


Remove del for email
 
B

Barry Schwarz

What if I make the pointer m_ptr is an extern ? In that case, passing
the pointer is optional.

If you only have one pointer you can make it global. But you said you
potentially had more than one.



Remove del for email
 
K

Keith Thompson

Barry Schwarz said:
snip
It's case 4, from Clive's other quoted statement,
which came out more messy than I remember sending it
(I miss my old newsreader):

We've lost the part you refer to but I believe the only thing of
interest is that a pointer p is indeterminate.
[cases 1..3 snipped]
(4) value is ignored, such as "(void) p" or "sizeof p".
[...]
Case 4 represents two different situations:

(void)p still causes p to be evaluated, even if the result of the
evaluation is discarded. This invokes undefined behavior. I'm not
sure but I expect that if the compiler optimizes the statement away
**completely**, then the undefined behavior caused by the evaluation
also disappears.

Right, except that the compiler is free to optimize the statement away
completely and replace it with code to cause demons to fly out of your
nose.

More reasonably, a compiler that generates extra run-time checks might
replace the evaluation of ``(void) p'' with code to cause a deliberate
run-time trap.
 
V

vippstar

We've lost the part you refer to but I believe the only thing of
interest is that a pointer p is indeterminate.

[cases 1..3 snipped]
(4) value is ignored, such as "(void) p" or "sizeof p".
[...]
Case 4 represents two different situations:
(void)p still causes p to be evaluated, even if the result of the
evaluation is discarded. This invokes undefined behavior. I'm not
sure but I expect that if the compiler optimizes the statement away
**completely**, then the undefined behavior caused by the evaluation
also disappears.

Right, except that the compiler is free to optimize the statement away
completely and replace it with code to cause demons to fly out of your
nose.

More reasonably, a compiler that generates extra run-time checks might
replace the evaluation of ``(void) p'' with code to cause a deliberate
run-time trap.

Is '(void)expr;' really invoking undefined behavior if expr's value
cannot be inspected?
What about 'expr;' without the cast?
A question perhaps not that related, are any of these two marked lines
valid:

....
void *p = NULL;
*p; //
(void)*p; //
....

gcc warns about 'dereferencing void * pointer', however it's a warning
and not an error.
 
S

santosh

(e-mail address removed) wrote:

A question perhaps not that related, are any of these two marked lines
valid:

...
void *p = NULL;
*p; //
(void)*p; //
...

I don't think so. Both the lines deference (or attempt to deference)
an "invalid" pointer value, i.e., NULL. This is mentioned in a footnote
in the standard in the section 6.5.3.2p4 (footnotes are not normative,
but the intent of the standard is clear). In such a case the behaviour
is undefined. Many modern systems will trap the access attempt but this
is not required by the standard itself.
gcc warns about 'dereferencing void * pointer', however it's a warning
and not an error.

It wouldn't be very useful, IMO, to make it an error because a null
pointer can always be constructed at runtime and deferenced. Also in
some system specific code, useful data may lie at machine address zero,
which may need to be accessed. For example under the x86 the interrupt
vectors start at address zero.
 
V

vippstar

(e-mail address removed) wrote:




I don't think so. Both the lines deference (or attempt to deference)
an "invalid" pointer value, i.e., NULL. This is mentioned in a footnote
in the standard in the section 6.5.3.2p4 (footnotes are not normative,
but the intent of the standard is clear). In such a case the behaviour
is undefined. Many modern systems will trap the access attempt but this
is not required by the standard itself.


It wouldn't be very useful, IMO, to make it an error because a null
pointer can always be constructed at runtime and deferenced. Also in
some system specific code, useful data may lie at machine address zero,
which may need to be accessed. For example under the x86 the interrupt
vectors start at address zero.

I was mainly interested in the 'void pointer dereferenced' part, not
that I dereference NULL. Let p have any value.
 
K

Keith Thompson

Is '(void)expr;' really invoking undefined behavior if expr's value
cannot be inspected?
Yes.

What about 'expr;' without the cast?

Also undefined behavior.
A question perhaps not that related, are any of these two marked lines
valid:

...
void *p = NULL;
*p; //

I had assumed that this was a constraint violation, but I can't
confirm that. The standard's description of the unary "*" operator
merely says:

The operand of the unary * operator shall have pointer type.

In any case, it invokes UB because it attempts to dereference a null
pointer. (There could be a constraint somewhere else that I'm
missing.)
(void)*p; //
Ditto.

gcc warns about 'dereferencing void * pointer', however it's a warning
and not an error.

The standard doesn't distinguish between warnings and errors.
 
B

Barry Schwarz

Barry Schwarz said:
It's case 4, from Clive's other quoted statement,
which came out more messy than I remember sending it
(I miss my old newsreader):
We've lost the part you refer to but I believe the only thing of
interest is that a pointer p is indeterminate.

[cases 1..3 snipped]
(4) value is ignored, such as "(void) p" or "sizeof p". [...]
Case 4 represents two different situations:
(void)p still causes p to be evaluated, even if the result of the
evaluation is discarded. This invokes undefined behavior. I'm not
sure but I expect that if the compiler optimizes the statement away
**completely**, then the undefined behavior caused by the evaluation
also disappears.

Right, except that the compiler is free to optimize the statement away
completely and replace it with code to cause demons to fly out of your
nose.

More reasonably, a compiler that generates extra run-time checks might
replace the evaluation of ``(void) p'' with code to cause a deliberate
run-time trap.

Is '(void)expr;' really invoking undefined behavior if expr's value
cannot be inspected?
What about 'expr;' without the cast?

This cast did not contribute anything to the issue. The undefined
behavior is caused by evaluating the indeterminate value.
A question perhaps not that related, are any of these two marked lines
valid:

...
void *p = NULL;
*p; //
(void)*p; //

Both are coding errors that *should* require a diagnostic. While the
standard doesn't ever say you cannot dereference a void*, it does say
that dereferencing a pointer to object type T results in an lvalue
designating the object and the lvalue has type T. Neither an lvalue
nor an object can have type void (it is an incomplete type that cannot
be completed).

If the compiler chooses to accept the source and produce an
executable, undefined behavior will occur when either statement is
executed as described in footnote 87.
...

gcc warns about 'dereferencing void * pointer', however it's a warning
and not an error.


Remove del for email
 
R

Richard Bos

Bartc said:
No. It just means that if accidentally a freed pointer is dereferenced, an
error is raised.

No, it doesn't. Or at least, it does not nearly guarantee that.
Otherwise it might quietly work until it causes trouble later on.

That's precisely my point: setting free()d pointers to null _will_
eventually lead you to overlook the situation where you have more than
one copy of that pointer value. You will have only set one of these to
null; the others will still be non-null, but invalid.
Now, suppose you are not in the habit of relying on pointers being
either null or valid. You will then make sure, because you have to, that
any pointer you use is valid. You will not assume that you can pass
random garbage pointers to functions and check later whether they're
null or not; you will make sure you _know_.
OTOH, suppose that you set all free()d pointers to null. You will then
believe that you don't have to take invalid pointers into account -
after all, if you do have to, what use is it to add the null-assigning
code? You do that work so you don't have to think. Good idea, but... bad
attempt. You'll pass some pointer to a function, that pointer will check
for null... and write to random memory, because you passed not the
original pointer, which got nulled, but a copy which didn't. _Now_ you
have a silent error. Not in the previous case, where you did your
bookkeeping.
You would also argue then that:

int *p = NULL;

is not a good idea?

I would argue that it is often over-used. It's a good idea if null is a
sensible initial value for that object. It is not a good idea if there
is no reason to read p between its declaration and its first assignment.

Richard
 
R

Richard Bos

Raymond Martineau said:
At least one platform requires you to dereference the equivalant of a
NULL pointer, since the BIOS stored an important table at that
location.

No, it doesn't. It stores an important table at 0x0000. Neither C nor
that platform requires the null pointer (it's not a NULL pointer, btw;
NULL is a macro which expands to a null pointer constant. _Especially_
where null pointers are concerned, precision is a necessary virtue in C
programmers) to have the bit pattern 0x0000, or to point at the location
where that table is stored.

Richard
 

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

No members online now.

Forum statistics

Threads
474,470
Messages
2,571,809
Members
48,797
Latest member
PeterSimpson
Top