Generalized function for deallocating and setting pointer to NULL

H

Harald van Dijk

But int* and void* are compatible.(?) Therefore..
DeallocAndNullify((void*)&p);
..should work.

int* and void* are not compatible any more than int and double are. There
are implicit conversions between the two, but portable code can't
reinterpret the bit pattern of one as the other.
 
C

CBFalconer

Keith said:
.... snip ...
....
int *p;
....
DeallocAndNullify(&p);
[...]

Nope. &p is of type int**; the parameter is of type void**.
There is no implicit conversion from int** to void**.

Also, your call is to DeallocAndNullify rather than
DeallocateAndNullify. (Perhaps you tried compiling your code, and
the misspelling caused the compiler not to know what the function
expects.)

Consider the following test (marked as quotation for linewrap):
[1] c:\c\junk>cat junk.c
#include <stdio.h>
#include <stdlib.h>

void release(void* *p) {
puts("Was Alive");
free(*p);
*p = NULL;
} /* release */

/* ------------------- */

int main(void) {
int *ptr;

if (ptr = malloc(sizeof *ptr)) release(&ptr);
if (ptr) puts("Alive");
else puts("Dead");

return 0;
} /* main, fcopylns */

[1] c:\c\junk>cc junk.c
junk.c: In function `main':
junk.c:15: warning: suggest parentheses around assignment used as truth value
junk.c:15: warning: passing arg 1 of `release' from incompatible pointer type

[1] c:\c\junk>a
Was Alive
Dead

Now I maintain the error is really a chimera. In release the void*
is never dereferenced, but is simply passed to free (which knows
what to do with it) and is set to NULL before exiting. The
parameter is simply a pointer to a void*.

Of course it would not do to ignore the error in passing the
parameter without analyzing the use in release, and that is
probably excessive detail.
 
S

santosh

CBFalconer said:
Keith said:
... snip ...
....
int *p;
....
DeallocAndNullify(&p);
[...]

Nope. &p is of type int**; the parameter is of type void**.
There is no implicit conversion from int** to void**.

Also, your call is to DeallocAndNullify rather than
DeallocateAndNullify. (Perhaps you tried compiling your code, and
the misspelling caused the compiler not to know what the function
expects.)

Consider the following test (marked as quotation for linewrap):
[1] c:\c\junk>cat junk.c
#include <stdio.h>
#include <stdlib.h>

void release(void* *p) {
puts("Was Alive");
free(*p);
*p = NULL;
} /* release */

/* ------------------- */

int main(void) {
int *ptr;

if (ptr = malloc(sizeof *ptr)) release(&ptr);

Maybe:

if (ptr = malloc(sizeof *ptr)) release( &((void *)ptr) );

<snip>
 
H

Harald van Dijk

Maybe:

if (ptr = malloc(sizeof *ptr)) release( &((void *)ptr) );

That won't compile. ((void *)ptr) is not an lvalue; you cannot take its
address.
 
J

James Kuyper

CBFalconer said:
Keith Thompson wrote: ....
Consider the following test (marked as quotation for linewrap):
[1] c:\c\junk>cat junk.c
#include <stdio.h>
#include <stdlib.h>

void release(void* *p) {
puts("Was Alive");
free(*p);
*p = NULL;
} /* release */

/* ------------------- */

int main(void) {
int *ptr;

if (ptr = malloc(sizeof *ptr)) release(&ptr);
if (ptr) puts("Alive");
else puts("Dead");

return 0;
} /* main, fcopylns */

[1] c:\c\junk>cc junk.c
junk.c: In function `main':
junk.c:15: warning: suggest parentheses around assignment used as truth value
junk.c:15: warning: passing arg 1 of `release' from incompatible pointer type

[1] c:\c\junk>a
Was Alive
Dead

Now I maintain the error is really a chimera. In release the void*
is never dereferenced, but is simply passed to free (which knows
what to do with it) and is set to NULL before exiting. The
parameter is simply a pointer to a void*.

The argument was a pointer to an int*, which means that even with a
suitable cast to silent the warning, *p==NULL has undefined behavior.
 
J

Joe Wright

James said:
Joe Wright wrote:
...

What made you think that?

Given..

int *ip;

We can do 'ip = malloc(sizeof *ip);

And now,

void *vp = ip;

...is valid. Why do you think not?
 
J

James Kuyper

Joe said:
Given..

int *ip;

We can do 'ip = malloc(sizeof *ip);

And now,

void *vp = ip;

..is valid. Why do you think not?

What you've just demonstrated is implicit conversion. Many types are
implicitly convertible to other types; section 6.3 is devoted to
describing the many ways in which this can happen. For instance, signed
char is implicitly convertible to long double _Complex. that doesn't
make them compatible.

The term "compatible types" is defined by the C standard in section
6.2.7, with cross-references to several other sections. Follow all of
those cross-references, and you won't find anything that makes "int*"
compatible with "void*".

There are only a few situations where you can access an object using an
lvalue of a one type, if the effective type of the actual object is
different. They are listed in 6.5p7, and the first one is if the lvalue
has " a type compatible with the effective type of the object". Because
int* is not compatible with void*, that case doesn't apply to this code.
Neither do any of the other cases listed in 6.5p7. Therefore, the
behavior of the program is undefined.

It might work as expected, that's one of the possibilities allowed when
the behavior of a program is undefined. On many implementations all
pointers have the same size, alignment, and representation, and on such
implementations this code could work. However, real implementations of C
have different representations for pointers to different types, and even
different sizes. This code will break on any such implementation.
 
K

Keith Thompson

CBFalconer said:
Consider the following test (marked as quotation for linewrap):
[1] c:\c\junk>cat junk.c
#include <stdio.h>
#include <stdlib.h>

void release(void* *p) {
puts("Was Alive");
free(*p);
*p = NULL;
} /* release */

/* ------------------- */

int main(void) {
int *ptr;

if (ptr = malloc(sizeof *ptr)) release(&ptr);
if (ptr) puts("Alive");
else puts("Dead");

return 0;
} /* main, fcopylns */

[1] c:\c\junk>cc junk.c
junk.c: In function `main':
junk.c:15: warning: suggest parentheses around assignment used as truth value
junk.c:15: warning: passing arg 1 of `release' from incompatible pointer type

[1] c:\c\junk>a
Was Alive
Dead

Now I maintain the error is really a chimera. In release the void*
is never dereferenced, but is simply passed to free (which knows
what to do with it) and is set to NULL before exiting. The
parameter is simply a pointer to a void*.

No, the error is a real error.

release() expects an argument of type void**. You give it an argument
of type int**. There is no implicit conversion from int** to void**,
so this is a constraint violation; the compiler is free to reject your
code on that basis, but it chooses instead to issue a warning and
(presumably) generate an implicit conversion anyway.

The language makes certain guarantees about the semantics of
conversions to and from void* (this is separate from the fact that
such conversions can be done implicitly). It makes no such guarantees
about conversions to or from void**, which is just another pointer
type. The conversion from int** to void** (a) is not specified by the
language, and (b) could conceivably lose information.

Now it's not likely that an implementation will actually use different
representations and/or argument passing mechanisms for void** vs.
int**, but it is entirely possible that an implementation could just
reject your code.

[...]
 

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
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top