473,729 Members | 2,234 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

The void** pointer breaking symmetry?

Hi Clers,

If I look at my ~200000 lines of C code programmed over the past 15
years, there is one annoying thing in this smart language, which
somehow reduces the 'beauty' of the source code ;-):

char *cp;
void *vp;
void **vpp;

// 1
cp=vp;

// 2
cp=*vpp;

Why is the first instruction allowed while the second one creates a
compiler warning/error?
If vpp is a pointer to a void pointer, why am I not allowed to assign
the content of vpp to a char pointer without ugly explicit casts? Why
is it needed to break the symmetry? Are there any GCC compiler options
to specifically disable this warning which doesn't make sense to me?

Thanks for your feedback,
Elmar

P.S.: In case this is an old question: Googles inability to search for
'void**' made it hard to find the answer ;-)

May 5 '06 #1
49 2792
el***@cmbi.ru.n l wrote:
If I look at my ~200000 lines of C code programmed over the past 15
years, there is one annoying thing in this smart language, which
somehow reduces the 'beauty' of the source code ;-):

char *cp;
void *vp;
void **vpp;

// 1
cp=vp;

// 2
cp=*vpp;

Why is the first instruction allowed while the second one creates a
compiler warning/error?
If vpp is a pointer to a void pointer, why am I not allowed to assign
the content of vpp to a char pointer without ugly explicit casts? Why
is it needed to break the symmetry? Are there any GCC compiler options
to specifically disable this warning which doesn't make sense to me?

This looks surprisingly similar to a discussion in van der Linden's
"Expert C Programming". There is a particular part in the Standard
regarding assignments that talks about specific constraints. I don't
have either handy right now, but the compiler error in this case feels
right.
May 5 '06 #2


el***@cmbi.ru.n l wrote On 05/05/06 14:44,:
Hi Clers,

If I look at my ~200000 lines of C code programmed over the past 15
years, there is one annoying thing in this smart language, which
somehow reduces the 'beauty' of the source code ;-):

char *cp;
void *vp;
void **vpp;

// 1
cp=vp;

// 2
cp=*vpp;

Why is the first instruction allowed while the second one creates a
compiler warning/error?
Both are legal, assuming vp has a valid value in the
first instance and vpp points to a void* with a valid
value in the second.

Perhaps the compiler is warning about the `=*', which
was an antique form of the operator now spelled `*='. If
so, it's trying to tell you that `=*' no longer means what
it did in the very early days of C (just in case you're
compiling some very old code), and you can probably silence
the warning by writing `= *' instead.

There are no guarantees, though: The compiler is allowed
to issue as many warnings as it wants, even for constructs
that are well-defined. Most people consider this helpful
in cases like

if (a = b)
printf ("Equal\n");
else
printf ("Unequal\n" ); /* Not any more ... */

.... which is a perfectly valid C fragment, but also a common
slip of the finger.
If vpp is a pointer to a void pointer, why am I not allowed to assign
the content of vpp to a char pointer without ugly explicit casts?
The assignment is allowed, and no cast is required.
Why
is it needed to break the symmetry? Are there any GCC compiler options
to specifically disable this warning which doesn't make sense to me?


Perhaps if you'd show us "this warning" instead of making
everybody guess about it, someone would have an idea.

--
Er*********@sun .com

May 5 '06 #3
Big apologies, I accidentally pasted only the 'correct' of the two
symmetry related cases:

Here is the complete code:

char *cp;
void *vp;
void **vpp;

// 1
cp=vp;
// 2
cp=*vpp;

// 3
vp=cp;
// 4
vpp=&cp;
In short: if 1 and 2 work, why does the reversion work in case 3 but
fail in case 4 with the GCC message "warning: assignment from
incompatible pointer type" ?

Thanks again,
Elmar

May 5 '06 #4
el***@cmbi.ru.n l writes:
char *cp;
void *vp;
void **vpp;

// 1
cp=vp;

// 2
cp=*vpp;

Why is the first instruction allowed while the second one creates a
compiler warning/error?


Both are allowed. If the compiler refuses to allow one of them
(possibly with a warning) then you are probably using a C++
compiler.
--
"Your correction is 100% correct and 0% helpful. Well done!"
--Richard Heathfield
May 5 '06 #5
el***@cmbi.ru.n l wrote:

If I look at my ~200000 lines of C code programmed over the past 15
years, there is one annoying thing in this smart language, which
somehow reduces the 'beauty' of the source code ;-):

char *cp;
void *vp;
void **vpp;

// 1
cp=vp;

// 2
cp=*vpp;

Why is the first instruction allowed while the second one creates a
compiler warning/error?
If vpp is a pointer to a void pointer, why am I not allowed to assign
the content of vpp to a char pointer without ugly explicit casts? Why
is it needed to break the symmetry? Are there any GCC compiler options
to specifically disable this warning which doesn't make sense to me?


You are probably confusing errors, because the above dereferences
an undefined pointer. The following is error free, and does not
dereference undefined objects:

int main(void) {

char *cp;
void *vp;
void **vpp;
char *s = "junk";

vp = &s[0];
cp = vp;
vpp = &vp;
cp = *vpp;

return 0;
}

Always publish complete compileable source, so criticism can be
meaningful.

--
"If you want to post a followup via groups.google.c om, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers." - Keith Thompson
More details at: <http://cfaj.freeshell. org/google/>
Also see <http://www.safalra.com/special/googlegroupsrep ly/>
May 5 '06 #6
el***@cmbi.ru.n l writes:
Big apologies, I accidentally pasted only the 'correct' of the two
symmetry related cases:

Here is the complete code:

char *cp;
void *vp;
void **vpp;

// 1
cp=vp;
// 2
cp=*vpp;

// 3
vp=cp;
// 4
vpp=&cp;
No, that's not the complete code. If I want to try compiling it
myself, I have to wrap it in a function definition.

Also, though "//" comments are legal in C99, they aren't supported by
all C compilers, and they can cause problems on Usenet (wrapping of
long lines can introduce syntax errors).

Here's a complete program that illustrates the point:

int main(void)
{
char *cp;
void *vp;
void **vpp;

/* 1 */
cp=vp;

/* 2 */
cp=*vpp;

/* 3 */
vp=cp;

/* 4 */
vpp=&cp; /* this is line 17 */

return 0;
}

When I compile this with gcc, I get:

tmp.c: In function `main':
tmp.c:17: warning: assignment from incompatible pointer type
In short: if 1 and 2 work, why does the reversion work in case 3 but
fail in case 4 with the GCC message "warning: assignment from
incompatible pointer type" ?


Case 1 assigns a void* to a char*. Implicit conversion from void* to
any pointer-to-object type makes this legal.

Case 2 also assigns a void* to a char*.

Case 3 assigns a char* to a void*. Implicit conversion from any
pointer-to-object type to void* makes this legal.

Case 4 assigns a char** to a void**. The implicit conversion rule
applies only to void*, not to void**. In this context, a void* is
just another object type, and a void** is just another
pointer-to-object type. There's no implicit conversion from one
pointer-to-object type to another pointer-to-object type.

void* is a generic pointer type. There is no generic
pointer-to-pointer type.

This is similar to the fact that there's an implicit conversion
between int and double, but no implicit conversion between int* and
double*.

If you want a generic pointer, just use a void*. For your case 4,
this would be legal:

vp = &cp;

--
Keith Thompson (The_Other_Keit h) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
May 5 '06 #7
Thanks for your detailed reply, Keith.

In my humble view, the ideal solution would be:

1) void* is a generic pointer type that can be implicitly converted
to/from any other object that can be dereferenced at least !once!
(e.g. char*, int*, also char**, int**, but not char,int)
2) void** is a generic pointer type that can be implicitly converted
to/from any other object that can be dereferenced at least !twice!
(e.g. char**, int**, also char***, int***, but not char*,int*,char ,int)
3) etc...

Again, I am not one of those who like to (have the time to) discuss
philosophic questions about language details. This is a purely
practical issue, having identified the main cause of entropy (and also
crashes ;-) in my current sources: The inability to safely pass a
pointer to any pointer as a function argument.

Typical example: the function mem_freesetnull which frees a pointer and
sets it to NULL (very helpful in the context of exception handling):

Ideally, it would take the address of a pointer as argument and look
like that:

void mem_freesetnull (void **ptradd)
{ mem_free(*ptrad d);
*ptradd=NULL; }

Unfortunately, that's not possible, because I'd have to use an ugly
explicit cast to (void**) in every call to the function.

So in practice, I have to move the explicit cast to the function
itself:

void mem_freesetnull (void *ptradd)
{ mem_free(*(void **)ptradd);
*(void**)ptradd =NULL; }

Now the function looks ugly, but more importantly, I lost an important
piece of type-safety:
If I accentally forget the reference operator & in the function call,
noone will complain but the program will crash:

char *cp;
cp=mem_alloc(10 00);
mem_freesetnull (cp); /* Crash! */

With the approach suggested above, the compiler would immediately
identify the problem, since cp cannot be dereferenced at least 2 times,
as required by the ideal function declaration
void mem_freesetnull (void **ptradd).

In short: less code entropy and more safety in one shot. It seems that
others are bothered by the same thing, since this in the FAQ:

4.9: Can I use a void ** pointer as a parameter so that a function
can accept a generic pointer by reference?

A: Not portably.

(not sure what the answer means in this context. Does it cause problems
on a VAX from 1968? ;-)

I'm thinking about a GCC patch for an option to specifically disable
the warning in the cases outlined above. But if that has zero chance of
acceptance, I'll save my time ;-)

Ciao and thanks,
Elmar

May 6 '06 #8
el***@cmbi.ru.n l schrieb:
Thanks for your detailed reply, Keith.
Of which you unfortunately did not quote anything to provide
context.
In my humble view, the ideal solution would be:

1) void* is a generic pointer type that can be implicitly converted
to/from any other object that can be dereferenced at least !once!
(e.g. char*, int*, also char**, int**, but not char,int)
2) void** is a generic pointer type that can be implicitly converted
to/from any other object that can be dereferenced at least !twice!
(e.g. char**, int**, also char***, int***, but not char*,int*,char ,int)
3) etc...

Again, I am not one of those who like to (have the time to) discuss
philosophic questions about language details. This is a purely
practical issue, having identified the main cause of entropy (and also
crashes ;-) in my current sources: The inability to safely pass a
pointer to any pointer as a function argument.
The problem with this approach is that if, for example, char *foo,
int *bar, and struct qux *quux have different sizes, alignment
requirements and representations , then there is no way that
void **baz (containing either the address of foo, bar, or quux, or
of a variable containing their addresses, or ...) helps you
deal with the object(s) they point to if you pass the addresses of
such pointers, because dereferencing the void ** variable once gives
you at least three possible interpretations of the bit pattern
involved. The only safe way to obtain the address of the object would
be if *baz would evaluate to a type which can contain any address that
can be pointed to by any of, foo, bar and quux. One such type is void*.
So,
void *aux = bar;
void **baz = &aux;
essentially is the best you can get if you do not want the language
to have to "remember" types at runtime.

Typical example: the function mem_freesetnull which frees a pointer and
sets it to NULL (very helpful in the context of exception handling):

Ideally, it would take the address of a pointer as argument and look
like that:

void mem_freesetnull (void **ptradd)
{ mem_free(*ptrad d);
*ptradd=NULL; }

Unfortunately, that's not possible, because I'd have to use an ugly
explicit cast to (void**) in every call to the function.

So in practice, I have to move the explicit cast to the function
itself:

void mem_freesetnull (void *ptradd)
{ mem_free(*(void **)ptradd);
*(void**)ptradd =NULL; }

Now the function looks ugly, but more importantly, I lost an important
piece of type-safety:
If I accentally forget the reference operator & in the function call,
noone will complain but the program will crash:

char *cp;
cp=mem_alloc(10 00);
mem_freesetnull (cp); /* Crash! */
Even
mem_freesetnull (&cp)
works portably only due to special guarantees for char*.
If you did the same for int *ip or struct qux *sp, you could run
into trouble on a system where not all pointers are equal.

With the approach suggested above, the compiler would immediately
identify the problem, since cp cannot be dereferenced at least 2 times,
as required by the ideal function declaration
void mem_freesetnull (void **ptradd).
Not portably if you do not want to change the information the
programme must have at runtime -- and then you could change other
restrictions of C as well.
In short: less code entropy and more safety in one shot.
At a very high price for the language.

In addition, not every place the now-stale address is stored will
be "nulled" by this function -- it is more or less a false sense
of safety. Realloc()ing to zero or even to a smaller size of the
originally malloc()ed storage gives you similar headaches.

Having a way to mark the starting address of the allocated object
and all addresses inside or one past the object as trap
representation with ways to find out where the whole thing trapped
would be much more useful.
Malloc debugging tools already do at least part of the job for you.
Just add "runs cleanly under <YourToolHere > for the test set" after
"compiles without warning" and "is <YourLintToolHe re>-clean".

It seems that
others are bothered by the same thing, since this in the FAQ:

4.9: Can I use a void ** pointer as a parameter so that a function
can accept a generic pointer by reference?

A: Not portably.

(not sure what the answer means in this context. Does it cause problems
on a VAX from 1968? ;-)

I'm thinking about a GCC patch for an option to specifically disable
the warning in the cases outlined above. But if that has zero chance of
acceptance, I'll save my time ;-)


As I did not read what you really want to achieve (you snipped the
context), I do not know whether this is the perfect solution for you
or just the bad idea it seems to be...
Cheers
Michael
--
E-Mail: Mine is an /at/ gmx /dot/ de address.
May 6 '06 #9
el***@cmbi.ru.n l wrote:
Thanks for your detailed reply, Keith.

In my humble view, the ideal solution would be:

1) void* is a generic pointer type that can be implicitly converted
to/from any other object that can be dereferenced at least !once!
(e.g. char*, int*, also char**, int**, but not char,int)
2) void** is a generic pointer type [...]
No; stop right there. void** is not at "generic" at all,
not in the least. A void** is a pointer to a void*, and not
a pointer to any other kind of object or function.

Your confusion, perhaps, is this: A void* can point to any
kind of object, and can be converted to and from other object
pointer types without a cast. That's why it's often called
"generic," but the term is really very loose. However, a void*
is itself an object type, a perfectly concrete "real" object
type like an int or a char* or whatever. Just as with other
concrete object types, it's possible to form a pointer to objects
of this void* type. But such a pointer is in no way "generic;"
it can only be NULL or point to an actual void* object somewhere.
[...] having identified the main cause of entropy (and also
crashes ;-) in my current sources: The inability to safely pass a
pointer to any pointer as a function argument.
That's right. C does not require that all pointers "smell
the same." Pointers to different types can come in different
shapes and sizes, so there's really no such thing as a "generic
pointer" (despite the common sloppy usage of the phrase to
describe void*). You might as well speak of a "generic number;"
just as short and double can look different, short* and double*
can look different.
Typical example: the function mem_freesetnull which frees a pointer and
sets it to NULL (very helpful in the context of exception handling):
Ideally, it would take the address of a pointer as argument and look
like that:

void mem_freesetnull (void **ptradd)
{ mem_free(*ptrad d);
*ptradd=NULL; }

Unfortunately, that's not possible, because I'd have to use an ugly
explicit cast to (void**) in every call to the function.
Even the cast will not save you. Just as numbers come in
different flavors, pointers come in different flavors. Just as
you cannot set a number to zero without knowing its type, you
cannot set a pointer to NULL without knowing its type.
So in practice, I have to move the explicit cast to the function
itself:

void mem_freesetnull (void *ptradd)
{ mem_free(*(void **)ptradd);
*(void**)ptradd =NULL; }

Now the function looks ugly, but more importantly, I lost an important
piece of type-safety:
Most important of all, the function is now incorrect.
You seem upset by all the warnings the compilers emit for
constructs of this sort, but it turns out they know C better
than you do: This code is wrong, and the compiler is right
to complain about it.
I'm thinking about a GCC patch for an option to specifically disable
the warning in the cases outlined above. But if that has zero chance of
acceptance, I'll save my time ;-)


While you're at it, disable the diagnostics for unbalanced
parentheses, for `("Hello" / "world!")', and for all the other
programming errors that might be made. The source for gcc is
readily available; you are free to make changes and use your own
version if you choose -- but allow me to suggest that your choice
is ill-informed. In short, you don't know what you're doing; you
don't know C well enough.

--
Eric Sosman
es*****@acm-dot-org.invalid
May 6 '06 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

23
22193
by: Ian Tuomi | last post by:
Hello, I was wondering, does it make any difference if you write void foo(int x) { /* insert code here */ } or foo(int x)
14
2469
by: Enrico `Trippo' Porreca | last post by:
Given: typedef struct Node Node; struct Node { void *obj; Node *next; }; typedef struct Stack Stack; struct Stack {
188
17386
by: infobahn | last post by:
printf("%p\n", (void *)0); /* UB, or not? Please explain your answer. */
14
3017
by: arun | last post by:
Hi, Why sizeof operator when applied on void returns one when compiled with gcc compiler ??. When i tried it on VC++ compiler, it gives an error. But another version of the VC++ compiler on my friend's machine gives it as zero. Have anyone tried this ? I believe it should give an error because i think there is nothing called void. Regards, arun..
28
16452
by: Peter Olcott | last post by:
I want to make a generic interface between a scripting language and native code, the native code and the interpreter will both be written in C++. The interpreter will probably be implemented as a subset of C/C++, thus will have the same syntax as C/C++. Somehow the interpreted code must be able to store generic function pointers because there is no way for the interpreter to know every possible function signature in advance. I was...
160
5651
by: raphfrk | last post by:
Is this valid? int a; void *b; b = (void *)a; // b points to a b += 5*sizeof(*a); // b points to a a = 100;
0
8761
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
9280
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
1
9200
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
8144
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
0
6016
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
4525
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
4795
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
3238
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
3
2162
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.