void* and void** compatibility

S

somenath

Is void * and void ** is compatible?
I do not get any compiler warning or error message for the first two functions (written below) but for the third one the compiler throws the followingwarning
gcc -ansi -pedantic -Wall test.c
test.c: In function ‘TestChar’:
test.c:17:5: warning: return from incompatible pointer type


#include<stdlib.h>
void **TestVoid()
{
void *p =malloc(2);
return p;
}

void *TestVoid2()
{
void **p =malloc(2);
return p;
}
char **TestChar()
{
char *p = malloc(2);
return p;
}



int main(void)
{
return 0;
}
It is clear that char* and char** is not compatible. But is it not true for void* and void**?
 
E

Eric Sosman

Is void * and void ** is compatible?

No. void* can be converted to or from any other (data) pointer
type without a cast, but "convertible to" is not the same as "compatible
with." A double can be converted to or from an int, but int and double
are not compatible.
I do not get any compiler warning or error message for the first two functions (written below) but for the third one the compiler throws the following warning
gcc -ansi -pedantic -Wall test.c
test.c: In function ‘TestChar’:
test.c:17:5: warning: return from incompatible pointer type


#include<stdlib.h>
void **TestVoid()
{
void *p =malloc(2);
return p;

Here the void* value is converted to a void** value. The
result may or may not be meaningful, but the conversion occurs
anyhow.
}

void *TestVoid2()
{
void **p =malloc(2);
return p;

Here the void** value is converted to a void* value. The
conversion always produces a valid void*, but since you can't do
much with a void* until you convert it to something else, the
big question is whether the "something else" would be valid --
and that's unknown at this point. Still, the conversion to
void* works.
}
char **TestChar()
{
char *p = malloc(2);
return p;

As you note below, char* and char** are not compatible.
The first points to a char, the second to a char*. Since char
and char* are different things, the thing that (validly) points
at one of them cannot (validly) point at the other.
}



int main(void)
{
return 0;
}
It is clear that char* and char** is not compatible. But is it not true for void* and void**?

void* and void** are incompatible, but void* is convertible
to or from anything else.
 
B

Barry Schwarz

Is void * and void ** is compatible?
I do not get any compiler warning or error message for the first two functions (written below) but for the third one the compiler throws the following warning
gcc -ansi -pedantic -Wall test.c
test.c: In function ‘TestChar’:
test.c:17:5: warning: return from incompatible pointer type


#include<stdlib.h>
void **TestVoid()
{
void *p =malloc(2);
return p;
}

The function you return to is entitled to expect the return value to
point to a void*. It is possible, but extremely unlikely, that the
void* could fit in the two bytes you have allocated.

The fact that the value of the void* is indeterminant provides another
chance to invoke undefined behavior.
void *TestVoid2()
{
void **p =malloc(2);
return p;
}

In this case, the type of p is irrelevant as long as it is some type
of object pointer. It could also be void*, char*, int**, or even
double(*)[5]. Whatever its type, it will be automatically and
silently converted to void*.
char **TestChar()
{
char *p = malloc(2);
return p;
}

If the function returned a void**, you would get the same error.

If p were of type void*, the compiler would not complain since a void*
can be converted to any other object pointer type.

But this code suffers from the same issues as TestVoid.
int main(void)
{
return 0;
}
It is clear that char* and char** is not compatible. But is it not true for void* and void**?

Actually, void* is compatible with any object pointer type. void** is
compatible only with void* and void**. Similarly, char* is compatible
only with void* and char*. And char** is compatible only with char**
and void*.
 
8

88888 Dihedral

Is void * and void ** is compatible?

I do not get any compiler warning or error message for the first two functions (written below) but for the third one the compiler throws the following warning

gcc -ansi -pedantic -Wall test.c

test.c: In function ‘TestChar’:

test.c:17:5: warning: return from incompatible pointer type





#include<stdlib.h>

void **TestVoid()

{

void *p =malloc(2);

return p;

}



void *TestVoid2()

{

void **p =malloc(2);

return p;

}

char **TestChar()

{

char *p = malloc(2);

return p;

}







int main(void)

{

return 0;

}

It is clear that char* and char** is not compatible. But is it not true for void* and void**?

A type of void * can be casted
explicitly into any pointer.

A type of void** is a pointer
to a pointer a of valid type.

Also void **img can be used
for mannual alocations of 2 D arrays.
 
K

Keith Thompson

Barry Schwarz said:
Actually, void* is compatible with any object pointer type. void** is
compatible only with void* and void**. Similarly, char* is compatible
only with void* and char*. And char** is compatible only with char**
and void*.

The word "compatible" has a specific meaning in C, and it's much
stricter than "implicitly convertible". The term is defined in
C11 6.2.7, with references to several other sections.
 
E

Eric Sosman

The function you return to is entitled to expect the return value to
point to a void*. It is possible, but extremely unlikely, that the
void* could fit in the two bytes you have allocated.

The fact that the value of the void* is indeterminant provides another
chance to invoke undefined behavior.

ITYM "indeterminate," but how so? The value is either NULL
or it points to two or more bytes of allocated memory; in either
case it is a perfectly valid pointer value. Furthermore, the
conversion to void** does no damage because of the alignment
guarantees for malloc(). True, the allocated space may be too
small to contain a void* value, but that just means that you've
got a valid pointer you mustn't dereference, not too unlike the
"one past the end" pointers one runs into with arrays.
Actually, void* is compatible with any object pointer type.

No: "compatible with" and "convertible to/from" are distinct
notions.
 
B

Barry Schwarz

ITYM "indeterminate," but how so? The value is either NULL
Yes.

or it points to two or more bytes of allocated memory; in either
case it is a perfectly valid pointer value. Furthermore, the

Your description applies to the value of p. The result of calling
malloc is just as you describe.

My comment was meant to apply to the void* that is pointed to by the
void** that TestVoid returns. Unfortunately, there are two void*, one
actual in the code and one conceptual in my response and I failed to
specify to which I was referring.
conversion to void** does no damage because of the alignment
guarantees for malloc(). True, the allocated space may be too
small to contain a void* value, but that just means that you've
got a valid pointer you mustn't dereference, not too unlike the
"one past the end" pointers one runs into with arrays.

It is permissible to evaluate the "one past" pointer, just not
dereference it. The void* pointed to by the return from TestVoid is
indeterminate (containing the two residual bytes of whatever memory
was allocated) or non-existent (if it cannot fit in the two bytes
allocated). Evaluating it for any purpose invokes undefined behavior.
 
E

Eric Sosman

Your description applies to the value of p. The result of calling
malloc is just as you describe.

My comment was meant to apply to the void* that is pointed to by the
void** that TestVoid returns. Unfortunately, there are two void*, one
actual in the code and one conceptual in my response and I failed to
specify to which I was referring.

Aha! Gotcha. But maybe it would be better not even to
consider that the pointed-to-by-void** object exists. Just as
we don't speak of "the object" pointed at by a NULL-valued
pointer, we shouldn't speak of "the object" residing in less
space than it occupies. Hence my misunderstanding: It never
occurred to me that there would be a void* object at the
pointed-to place.
 
J

JohnF

Keith Thompson said:
The word "compatible" has a specific meaning in C, and it's much
stricter than "implicitly convertible". The term is defined in
C11 6.2.7, with references to several other sections.

Just to clear this up for me in an explicit "yes or no" way,
to which I hope the answer is "yes", void *ptr can be explicitly
cast any which way you like and will work correctly, is that right?
For example (omitting some obvious #define's and various other
housekeeping), this will work correctly, right?...
void func ( void *ptr, int whatisit, etc ) {
switch ( whatisit ) {
case ITSANINTP: /* easy, I think */
int myint = *((int *)ptr);
etc; break;
case ITSASTRUCTPP: /* maybe harder */
struct MYSTRUCT *mystruct = *((struct MYSTRUCT **)ptr);
mystruct->whatever = anything;
etc; break;
default: break; }
etc;
return; }
and even a struct MYSTRUCT *** could be cast and dereferenced
correctly, with a few more asterisks and parens, right?
And this would be completely portable, among standards-compliant
compilers? Thanks,
 
B

Ben Bacarisse

JohnF said:
Just to clear this up for me in an explicit "yes or no" way,
to which I hope the answer is "yes", void *ptr can be explicitly
cast any which way you like and will work correctly, is that right?

If you force a yes/no answer, it's no. I'd have gone for kind of. The
conversion is usually fine, but the resulting value may not work
correctly. I say "usually", because the pointer might hold a trap
representation -- a sort of always invalid non-value -- though such
things are relatively rare these days.
For example (omitting some obvious #define's and various other
housekeeping), this will work correctly, right?...
void func ( void *ptr, int whatisit, etc ) {
switch ( whatisit ) {
case ITSANINTP: /* easy, I think */
int myint = *((int *)ptr);
etc; break;
case ITSASTRUCTPP: /* maybe harder */
struct MYSTRUCT *mystruct = *((struct MYSTRUCT **)ptr);
mystruct->whatever = anything;
etc; break;
default: break; }
etc;
return; }
and even a struct MYSTRUCT *** could be cast and dereferenced
correctly, with a few more asterisks and parens, right?
And this would be completely portable, among standards-compliant
compilers? Thanks,

The main thing that can go wrong is that the address may not be
correctly aligned for the access being done, but there are other
potential problems as well. For example, the object pointed to may no
longer exist. But if you can ensure that the pointer is in fact valid
for the type of access involved, then this sort of code will work.

However, I would try to avoid it. If you can get by with a union then
that would be my preferred option but I don't know what you are doing so
there may be no better solution.
 
E

Eric Sosman

Just to clear this up for me in an explicit "yes or no" way,
to which I hope the answer is "yes", void *ptr can be explicitly
cast any which way you like and will work correctly, is that right?

Sort of. Yes, a void* can be explicitly cast to and from
any other data (not function) pointer type. Also, a void* will
convert to and from any other data pointer type implicitly,
with no cast required.

It's the "will work correctly" part that's not so certain.
Here's what you can do: Starting with a valid Anything* pointer,
you can convert it (implicitly or by cast) to a void* and then
convert the void* to another Anything* again, and the two Anything*
values will compare equal, will point at the same Anything object,
and so on. BUT you can't always go the other way: If you start
with a void* value, convert to Anything*, and convert to void*
again, the two void* values might not be equal if the original
wasn't aimed at someplace where an Anything could reside. If an
Anything has a stricter-than-byte alignment requirement, that
requirement may be reflected in how an Anything* is represented.
If so, and if the original void* target doesn't satisfy Anything's
alignment, the conversion to Anything* may lose information. (It
may even trap or do something nastier.)

So: "Will work correctly" if the pointer targets are legitimate
for all the types involved, but otherwise all bets are off.
For example (omitting some obvious #define's and various other
housekeeping), this will work correctly, right?...
void func ( void *ptr, int whatisit, etc ) {
switch ( whatisit ) {
case ITSANINTP: /* easy, I think */
int myint = *((int *)ptr);
etc; break;
case ITSASTRUCTPP: /* maybe harder */
struct MYSTRUCT *mystruct = *((struct MYSTRUCT **)ptr);
mystruct->whatever = anything;
etc; break;
default: break; }
etc;
return; }
and even a struct MYSTRUCT *** could be cast and dereferenced
correctly, with a few more asterisks and parens, right?
And this would be completely portable, among standards-compliant
compilers? Thanks,

Looks okay, provided the value of ptr is in fact suitable
for whichever case gets chosen.
 
J

James Kuyper

Just to clear this up for me in an explicit "yes or no" way,
to which I hope the answer is "yes", void *ptr can be explicitly
cast any which way you like and ...

Not quite. You can cast it to an integer type, but not to a floating
point type, nor to a struct, union, or array type. You can cast it to a
pointer to any object type, including a pointer to any of those types.
However, you can't cast it to a pointer to a function type unless it's null.

.... > will work correctly, is that right?

That depends upon what you do with the converted pointer.

Any pointer value may be converted to any integer type; in general the
result is implementation-defined - how useful that is depends upon how
the implementation choses to define it. There's only three portably
useful integer types you can convert to: converting to _Bool is
equivalent to (value ? true : false). Converting a void* value to
intptr_t produces a value that, if converted back to void*, will
produce a value equivalent to the original pointer.

Any two void* pointers that don't have trap representations can be
compared for equality.

If you converted it to another pointer type, you can convert it back to
void* - but the standard doesn't say anything useful about the resulting
pointer. In particular, it does not guarantee that the result will be
equivalent to the original pointer.

The safety of all other uses of the converted pointer depends upon how
the void* value was originally obtained.

If you obtained the void* by conversion of a pointer to an object type,
conversion back to the original pointer type is guaranteed to produce a
pointer equivalent to the original.

If there is a single object such that two different void* pointers each
point at the object, or at the end of the object, or at a sub-object of
that object, then those pointers can be compared for relative order.

If the void* pointer was null, the converted pointer will also be null,
and therefore cannot be dereferenced or usefully compared for relative
order with other pointers.

Conversion of an integer constant expression with a value of 0 to void*
results in a null pointer, which I've already covered. Otherwise,
conversion of an arbitrary integer value to void* is allowed, but the
result might be a trap representation. If converted back to the original
integer type, the standard says nothing about the resulting value; in
particular, it is not guaranteed to be equivalent to the original value.

If the void* was returned by a call to malloc(), calloc(), or realloc(),
it is guaranteed to be correctly aligned for conversion to a pointer to
any object type. If you allocated sufficient memory, that pointer can be
used to store a corresponding object of that type.
For example (omitting some obvious #define's and various other
housekeeping), this will work correctly, right?...
void func ( void *ptr, int whatisit, etc ) {
switch ( whatisit ) {
case ITSANINTP: /* easy, I think */
int myint = *((int *)ptr);
etc; break;
case ITSASTRUCTPP: /* maybe harder */
struct MYSTRUCT *mystruct = *((struct MYSTRUCT **)ptr);
mystruct->whatever = anything;
etc; break;
default: break; }
etc;
return; }
and even a struct MYSTRUCT *** could be cast and dereferenced
correctly, with a few more asterisks and parens, right?
And this would be completely portable, among standards-compliant
compilers? Thanks,

As long as your ITSA*P macros mean what they seem to mean, and if
whatisit has a correct value, that should be fine.
 
J

JohnF

Thanks, Ben, Eric, James, ...
-----------------------------
If you force a yes/no answer, it's no. I'd have gone for kind of.
The conversion is usually fine, but the resulting value may not work
correctly. I say "usually", because the pointer might hold a trap
representation -- a sort of always invalid non-value -- though such
things are relatively rare these days.

Don't think that kind of thing is a problem. My usage would
always be (as far as I can recall ever using) of the general form,
calling that func() example below,
int main ( int argc, char *argv[] ) {
ANYTHING *ptr; /* using James' "Anything" example terminology */
/* typically... */
func ( ptr, ITSANANYTHINGP, etc);
/* or, if I was being more careful, which I wasn't always... */
func ( (void *)ptr, ITSANANYTHINGP, etc);
etc; }
Did James say float/double wouldn't work this way, i.e.,
ANYTHING=double is a no-no??? I don't recall ever using double
in this kind of context, but I wouldn't have (maybe until now)
avoided it if needed.
The main thing that can go wrong is that the address may not be
correctly aligned for the access being done,

So that's not a problem in above usage example,
as far as I can tell, right?
but there are other potential problems as well.
For example, the object pointed to may no longer exist.

Ditto, I think, not a problem? If I understand correctly,
both these potential problems reflect some other bug in your
program, i.e., these problems won't crop up unless you've
done something else wrong, and then it's a side-effect
of your other wrong thing.
But if you can ensure that the pointer is in fact valid
for the type of access involved, then this sort of code will work.
Terrific. I think that's what I was hoping to hear.
However, I would try to avoid it. If you can get by with a union then
that would be my preferred option but I don't know what you are doing so
there may be no better solution.

Not doing anything like this at the moment. But I've got lots of old code
that already does it, and that seems to be working, mostly on linux/unix/gcc
and on windows/mingw/djgpp, but which I've >>assumed<< to be completely
portable. And I just wanted to double-check that, both for old code
and for future reference. The remarks in this thread were leaving me
a little less certain than I had been.
And just to check -- you're saying "union" so the compiler will solve
any potential alignment problems before this kind of code trips
over them, right? Any other "union" reason? Thanks,
 
J

James Kuyper

On 12/18/2013 03:44 AM, JohnF wrote:
....
Don't think that kind of thing is a problem. My usage would
always be (as far as I can recall ever using) of the general form,
calling that func() example below,
int main ( int argc, char *argv[] ) {
ANYTHING *ptr; /* using James' "Anything" example terminology */
/* typically... */
func ( ptr, ITSANANYTHINGP, etc);
/* or, if I was being more careful, which I wasn't always... */
func ( (void *)ptr, ITSANANYTHINGP, etc);
etc; }
Did James say float/double wouldn't work this way, i.e.,
ANYTHING=double is a no-no??? I don't recall ever using double
in this kind of context, but I wouldn't have (maybe until now)
avoided it if needed.

No, I was saying that (double)ptr has undefined behavior. (double*)ptr
is perfectly fine, assuming that ptr contains the result of converting a
valid double* pointer to void*.
Ditto, I think, not a problem? If I understand correctly,
both these potential problems reflect some other bug in your
program, i.e., these problems won't crop up unless you've
done something else wrong, and then it's a side-effect
of your other wrong thing.

Not really. There's nothing wrong, in itself, with having a pointer that
points at an object that no longer exists. However, attempting to read
that pointer has undefined behavior.
And just to check -- you're saying "union" so the compiler will solve
any potential alignment problems before this kind of code trips
over them, right? Any other "union" reason? Thanks,

C has anti-aliasing rules, that allow a compiler to optimize code by
assuming that a pointer to long, for instance, will never point at the
same location as a pointer to double.
Within the scope of the declaration of such a union, code which might
refer to members of such a union object is allowed, that would otherwise
violate the anti-aliasing rules.
 
B

Ben Bacarisse

JohnF said:
Thanks, Ben, Eric, James, ...
-----------------------------
If you force a yes/no answer, it's no. I'd have gone for kind of.
The conversion is usually fine, but the resulting value may not work
correctly. I say "usually", because the pointer might hold a trap
representation -- a sort of always invalid non-value -- though such
things are relatively rare these days.

Don't think that kind of thing is a problem. My usage would
always be (as far as I can recall ever using) of the general form,
calling that func() example below,
int main ( int argc, char *argv[] ) {
ANYTHING *ptr; /* using James' "Anything" example terminology */
/* typically... */
func ( ptr, ITSANANYTHINGP, etc);
/* or, if I was being more careful, which I wasn't always... */
func ( (void *)ptr, ITSANANYTHINGP, etc);

The cast is not needed if there is a prototype for func in scope. It's
generally better to avoid a cast where it is not needed -- that way the
presence of a cast suggests something noteworthy is happening.
etc; }
Did James say float/double wouldn't work this way, i.e.,
ANYTHING=double is a no-no??? I don't recall ever using double
in this kind of context, but I wouldn't have (maybe until now)
avoided it if needed.

No, he was talking about converting the pointer to double or float.
Converting a void * to a double * (if the pointer does indeed point to a
double) is fine.
So that's not a problem in above usage example,
as far as I can tell, right?

Yes, the alignment will be fine.
Ditto, I think, not a problem? If I understand correctly,
both these potential problems reflect some other bug in your
program, i.e., these problems won't crop up unless you've
done something else wrong, and then it's a side-effect
of your other wrong thing.

That's right.
Terrific. I think that's what I was hoping to hear.


Not doing anything like this at the moment. But I've got lots of old code
that already does it, and that seems to be working, mostly on linux/unix/gcc
and on windows/mingw/djgpp, but which I've >>assumed<< to be completely
portable. And I just wanted to double-check that, both for old code
and for future reference. The remarks in this thread were leaving me
a little less certain than I had been.
And just to check -- you're saying "union" so the compiler will solve
any potential alignment problems before this kind of code trips
over them, right? Any other "union" reason? Thanks,

Not really. I was making what might have been a rather too-general
point that code of this sort -- it's a sort of poor programmer's
polymorphic function -- is often better written to operate on a union
object. That might be quite wrong in this case, but it's not relevant
since you have lots of code like this so a re-design is not
appropriate.
 
J

JohnF

Thanks again, Ben, James, ...
-----------------------------
My usage would
always be (as far as I can recall ever using) of the general form,
calling that func() example below,
int main ( int argc, char *argv[] ) {
ANYTHING *ptr; /* using Eric's "Anything" example terminology */
/* typically... */
func ( ptr, ITSANANYTHINGP, etc);
/* or, if I was being more careful, which I wasn't always... */
func ( (void *)ptr, ITSANANYTHINGP, etc);
etc; }

The cast is not needed if there is a prototype for func in scope.
Yes-and-no. My inline (not in included .h files) prototypes are
frequently of the not-so-great form void func(); without explicitly
specifying func's args (and gcc doesn't complain about that,
even with -pedantic -Wall).

Anyway, it sounds in general like void *ptr can safely and portably
be re-cast any which way I've ever done or am likely ever to think
about doing. Of course, how often have I said that kind of thing
before, just to be soon proven wrong (answer: lots)? Thanks,
 
B

Ben Bacarisse

JohnF said:
Ben Bacarisse <[email protected]> wrote:
Yes-and-no. My inline (not in included .h files) prototypes are
frequently of the not-so-great form void func(); without explicitly
specifying func's args (and gcc doesn't complain about that,
even with -pedantic -Wall).

That's not a prototype. C distinguishes between a plain declaration and
a declaration that is a prototype. You can get gcc to warn you about
declarations that are not prototype with -Wstrict-prototypes. It's a
technicality of naming that I should have pointed out when I made the
remark.

<snip>
 
J

JohnF

Ben Bacarisse said:
That's not a prototype. C distinguishes between a plain declaration and
a declaration that is a prototype. You can get gcc to warn you about
declarations that are not prototype with -Wstrict-prototypes. It's a
technicality of naming that I should have pointed out when I made the
remark.

Thanks for the clarification. Now that you mention it, it makes sense.
But that distinction just hadn't crossed my mind in the past.
 
T

Tim Rentsch

Eric Sosman said:
Keith Thompson said:
[...]
Actually, void* is compatible with any object pointer type. void** is
compatible only with void* and void**. Similarly, char* is compatible
only with void* and char*. And char** is compatible only with char**
and void*.

The word "compatible" has a specific meaning in C, and it's much
stricter than "implicitly convertible". The term is defined in
C11 6.2.7, with references to several other sections.

Just to clear this up for me in an explicit "yes or no" way,
to which I hope the answer is "yes", void *ptr can be explicitly
cast any which way you like and will work correctly, is that right?

Sort of. Yes, a void* can be explicitly cast to and from
any other data (not function) pointer type. Also, a void* will
convert to and from any other data pointer type implicitly,
with no cast required. [snip unrelated]

The second sentence there is a little off. A void* will
convert implicitly /to/ any other data pointer type, but it
will convert implicitly /from/ another data pointer type only
when the other referenced type is unqualified, eg, if the other
pointer type is 'const double *' or 'volatile int *' then
implicit conversion is not allowed and a cast is required.
 
T

Tim Rentsch

James Kuyper said:
Keith Thompson said:
[...]
Actually, void* is compatible with any object pointer type.
void** is compatible only with void* and void**. Similarly,
char* is compatible only with void* and char*. And char** is
compatible only with char** and void*.

The word "compatible" has a specific meaning in C, and it's much
stricter than "implicitly convertible". The term is defined in
C11 6.2.7, with references to several other sections.

Just to clear this up for me in an explicit "yes or no" way, to
which I hope the answer is "yes", void *ptr can be explicitly cast
any which way you like and ...

Not quite. You can cast it to an integer type, but not to a
floating point type, nor to a struct, union, or array type. You can
cast it to a pointer to any object type, including a pointer to any
of those types. However, you can't cast it to a pointer to a
function type unless it's null. [snip unrelated]

Except for the special case of a null pointer constant, a pointer
of type (void*) need not be convertible to a pointer to function
whether or not it is null. Conversion between pointers to
function types and pointers to non-function types is not strictly
prohibited but it isn't required either (again except for the
special case of a null pointer constant), and AFAIK it typically
is not allowed in conforming implementations, eg, it is flagged
by the compiler as an error (and independently of whether the
value being converted might be null).

Under these circumstances the inference that converting a null
non-function pointer type must produce a null function pointer
type, or vice versa, is IMO not a reliable conclusion. The view
expressed in the last quoted sentence above is certainly not
universally shared in the C development or implementation
communities.
 

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,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top