473,757 Members | 2,284 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
49 2802
el***@cmbi.ru.n l wrote:
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 [...]


We have a generic pointer type: void *

What if you want to point to one of these generic pointers?
And you are a person who thinks type-safety has advantages
(which is presumably why you chose C instead of Perl for
your program).

You want to have a non-generic pointer that can point to
generic pointers.

After all, if you wanted to use a generic pointer to point to
a generic pointer.... you would just use the generic pointer
you already have.

The designers of C presumably followed this logic, and
decided that void ** should be a pointer to void* (and not
a pointer to anything else).
there are two types of programming brains:

#define FOOS 10
struct
{ int i;
double d;
char *p; } *foo;

foo=calloc(FOOS ,sizeof(*foo));

Looking at the piece of code above, brain type A will cry in pain
and quickly change the wrong code to read

foo=malloc(FOOS *sizeof(*foo));
for (i=0;i<FOOS;i++ )
{ foo[i].i=0;
foo[i].d=0;
foo[i].p=NULL; }

And then, there's brain type B who sees that the 'correct' version
takes 5 lines instead of 1, reduces readability, maintainability , can be
a source of additional errors and wastes developer life time.
Type B simply uses the 'elegant' calloc approach and trusts in the
laws of physics:
Well, if I were an employer I'd rather have programmer B work for me.

I guess you have never had to port an application from one
platform, to another substantially different one.

In fact I would prefer programmer D who writes:

struct bar
{
int i;
double d;
char *p;
};
static struct bar const bar_defaults = { 0 };

struct bar *p_foo = malloc( FOOS * sizeof *p_foo );
if (!p_foo) do_something... ..

for (size_t i = 0; i != FOOS; ++i)
p_foo[i] = bar_defaults;

which avoids the problem of the structure being updated later
and the initialization step forgotten. Also it allows for a field
to have a non-zero default value, something which is
impossible in the inflexible B-brain version.

Further, if the defaults is indeed all bits zero, the compiler
will be likely to generate a memset instruction (or even
call calloc).
even if one night, someone drunk and stoned
creates an architecture that requires the long version,
it will be forced to play a marginal niche role, since an architecture
that is so inherently inefficient that it requires five times as much
code cannot rule the world.


Who measures an architecture's efficiency in lines of C code ??

I think you would get on well with Paul Hsieh (another regular
poster here who thinks that not only is catering for non-x86 a
waste of time, but he must also admonish anybody else who
does it).

May 8 '06 #21
el***@cmbi.ru.n l said:
#define FOOS 10
struct
{ int i;
double d;
char *p; } *foo;

foo=calloc(FOOS ,sizeof(*foo));

Looking at the piece of code above, brain type A will cry in pain
Not having read the entire thread, I don't know what you mean by brain type
A, but I would certainly consider that the calloc is misguided.
and quickly change the wrong code to read

foo=malloc(FOOS *sizeof(*foo));
for (i=0;i<FOOS;i++ )
{ foo[i].i=0;
foo[i].d=0;
foo[i].p=NULL; }
Well then, I'm not Brain Type A, because that isn't how I'd change it /at
all/!
And then, there's brain type B who sees that the 'correct' version
takes
5 lines instead of 1, reduces readability, maintainability , can be
a source of additional errors and wastes developer life time.
Type B simply uses the 'elegant' calloc approach and trusts in the
laws of physics


Weird architectures do not in fact break the laws of physics. They just
break the law of familiarity.

But anyway, I can see from the above verbiage that I'm not Brain Type B
either.

Being, perhaps, of Brain Type C, I prefer to do this in what we like to call
"the right way".

The following assumes you require a deterministical ly blank collection of
struct FOO - if you know what you want to write into the members of the
collection, which really you ought to before creating the collection in the
first place, then replace blankfoo appropriately:

struct FOO *foo_array_crea te(size_t n)
{
struct FOO blankfoo = {0};

foo = malloc(n * sizeof *foo); /* note the removal of the stupid constant,
* and the redundant parentheses.
*/
if(foo != NULL) /* note the error checking */
{
for(i = 0; i < n; i++)
{
foo[i] = blankfoo; /* note the non-necessity for knowledge
* about the innards of the struct type */
}
}
return foo;
}

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
May 10 '06 #22
Keith Thompson wrote:
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.


Is it suitable and helpful also to understand the following question:

void foo1(const char *p) {}
void foo2(const char **p) {}

int main(void)
{
char *p1;
char **p2;

foo1(p1); /* line 9: consistent type conversion */
foo2(p2); /* line 10: inconsistent type conversion */
}

May 10 '06 #23
lovecreatesbeau ty said:
Is it suitable and helpful also to understand the following question:

void foo1(const char *p) {}
void foo2(const char **p) {}

int main(void)
{
char *p1;
char **p2;

foo1(p1); /* line 9: consistent type conversion */
foo2(p2); /* line 10: inconsistent type conversion */
}


It's okay to pass a char * to a function expecting const char *, because all
the function is doing is promising not to write to the object pointed at.

But your second example passes a pointer to pointer to char, to a function
which is expecting a pointer to a const pointer to char. In other words,
the function is asking for a guarantee that the pointer you pass it is a
pointer to a pointer value through which writing is not allowed. This is
not an unreasonable thing to ask - for example, it might use the pointer
value internally in a way that would be corrupted if that pointer value
changed.

Please remember that there is an important difference between const char **p
and char * const * p.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
May 10 '06 #24
Old Wolf wrote:

Well, if I were an employer I'd rather have programmer B work for me.


Should be 'A'.

May 10 '06 #25
Old Wolf said:
el***@cmbi.ru.n l wrote:
there are two types of programming brains:

#define FOOS 10
struct
{ int i;
double d;
char *p; } *foo;

foo=calloc(FOOS ,sizeof(*foo));

Looking at the piece of code above, brain type A will cry in pain
and quickly change the wrong code to read

foo=malloc(FOOS *sizeof(*foo));
for (i=0;i<FOOS;i++ )
{ foo[i].i=0;
foo[i].d=0;
foo[i].p=NULL; }

And then, there's brain type B who sees that the 'correct' version
takes 5 lines instead of 1, reduces readability, maintainability , can be
a source of additional errors and wastes developer life time.
Type B simply uses the 'elegant' calloc approach and trusts in the
laws of physics:


Well, if I were an employer I'd rather have programmer B work for me.


(You correct this to 'A' in a followup.)

I'd rather employ someone who could get the code right. In my opinion,
neither of the above achieved this.
--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
May 10 '06 #26

Ben C wrote:
["Brain Type B":]
#define FOOS 10
struct
{ int i;
double d;
char *p; } *foo;

foo=calloc(FOOS ,sizeof(*foo));
Looking at the piece of code above, brain type A will cry in pain
and quickly change the wrong code to read

foo=malloc(FOOS *sizeof(*foo));
for (i=0;i<FOOS;i++ )
{ foo[i].i=0;
foo[i].d=0;
foo[i].p=NULL; }

And then, there's brain type B who sees that the 'correct' version
takes 5 lines instead of 1, reduces readability, maintainability , can
be a source of additional errors and wastes developer life time. Type
B simply uses the 'elegant' calloc approach [...]


Practically speaking it's much more likely that someone will add a field
to the struct and forget to update the type A setup code (this happens
really quite often) than that the code will get ported to a machine on
which NULL pointers, 0 and 0.0 aren't all represented by zero bits.


YEEEESS! That's my opinion, finally, it's not me against the rest
anymore ;-)
There's a saying "premature optimization is the root of all evil".
Premature paranoia is pretty bad too. Anything, premature or not, that
reduces readability or simplicity is going to introduce bugs, and many
of those bugs will work every day on everyday machines.

Sloppiness (e.g. not checking errors at all) is obviously unacceptable;
but the naive conclusion that "the more error or paranoia checking the
better" is almost as bad if it's done at the price of untested error
recovery paths or complex and difficult-to-maintain initialization
routines.


100% acknowledged, looks like another brain type B is around...

Cheers,
Elmar

May 10 '06 #27
Richard Heathfield wrote:

I'd rather employ someone who could get the code right. In my opinion,
neither of the above achieved this.


Well, I agree. But I think A is an improvement on B. And I'd also
guess that programmer 'A' would be more amenable to being taught
the correct way than programmer 'B' would (who, if this guy is
anything to go by, would resist or ignore the advice given).

May 10 '06 #28
> > Indeed, I am not used to the 'brain type A' way of thinking where it's
common to expand the source code with one additional 'if statement' per
memory allocation. Of course the callocs/mallocs above are not those
from libc. (Mine are called mem_alloc and mem_calloc, and I didn't want
to cause additional confusion, but I'll do it from now ;-). Anyway,
mem_alloc and friends do of course never return a NULL pointer.
Instead, you can register emergency cleanup functions (that for example
try to save any open documents) directly in the memory manager. Keep in
mind that if the OS denies even small memory allocation requests, no
program with a decent user interface can continue to do any kind of
useful work. For truly big memory allocations, there's mem_allocnull
and friends, that may return a NULL pointer, which you check and in the
worst case tell the user that this specific operation needs more RAM or
a bigger swap file.
Hmmm, and how do you make sure that you never inadvertently try to
mem_alloc() too much because of an erroneous size computation
beforehand?


If the size computation is wrong, then we got a serious bug and it's OK
if mem_alloc calls the registered rescue&cleanup functions and exits,
no?
In addition, it causes less code changes if the requirements change:
You already have your error handling code in place, have tested it
once, have enough information (no additional parameters to wire the
code with) so that you can change strategy, request a user input,
die gracefully, or whatever is necessary. From my previous experiences, it's always the truly big multi-megabyte allocations that can fail, not the little every-day things. I even
somehow suspect that the OS doesn't use a hard cutoff but says 'OK,
even though you are over the limits, you get another MB. What, you want
another 100MB? Die!'
I have seen enough huge simulations die on the last couple of time
steps without storing an intermediate state of computation before
dieing for things like forgotten checks after demanding "only a couple
of bytes from the heap"... Reading "paranoia check omitted for speedup"
or similar makes you wish to throttle the culprit...
Well, then the callbacks in mem_alloc would have saved your huge
simulation, assuming that the OS still allowed to open a file, FILE*
also needs space ;-)
I started out as type B and made my way over to something between type
A and Eric's type C.
Between A and C, there's B ;-))
Then he might well have
other cases in which a foothing needs to be initialiazed, so he
would make the definitions more usable:

struct foo {
int i;
double d;
char *p;

} *foop;

void initfoo(struct foo *foop) {
foop->i = foop->d = 0; foop->p = NULL;

} /* initfoo */
I don't agree. If you know the big picture in advance, you also know
that a certain structure is guaranteed to be needed only once, and then
the fooing above just costs your life time and randomizes the code
order.


If it is needed only once, then there is no need for allocation.


Depends on the size, if it's large and you make it static, you waste
memory. If you put it on the local stack, it may not be portable due to
stack size restrictions ;-(
Well, even though Chuck tends to rather harsh wording, he has a
point here:
I'd rather work with type A or C than with type B as type B's
inadvertent mistakes could cost _my_ lifetime and count against my
frustration tolerance threshold when it comes to a customer fuming
over a stupid mistake costing hours of work.
I would replace 'inadvertent mistakes' with 'restriction to a relevant
subset of the C standard'.
If type B documented all his or her decisions for more efficient
code together with some reasoning why it is safe to do here, then
this would cost more time than doing all the "overhead stuff" in
the first place.
It is all documented in the README, and takes 5 lines:

The following architecture assumptions and programming guidelines were
found to reduce
source code length by 40%, and have thus been choosen as a
prerequisite:
*) The binary representation of 0 or NULL must indeed be 0 for all
datatypes.
*) All pointers must have the same size.
*) Never use side-effects ++/-- on function arguments.
BTW: It is completely acceptable to make some assumptions like
"8 bit bytes", "2s complement exact width integer types", "all
pointer types have the same type and representation and alignment
requirements", etc. -- if you document them and have a compile
time test module and a run-time test module making sure these
assumptions are justified.
Yep, got that.
Then your code is an island of security.
However, this means that even small parts of the code must be
assumed to rely on these assumptions which may make the code
unusable for projects without the respective assumptions.
For small projects, rewriting may be a good idea; for large
projects, you get your personal regression test nightmare.


While that's true, I am confident that it will never happen. Instead I
think that inefficient architectures with non-zero NULL pointers etc.
will continue to disappear, and one day the C standard will be changed
to avoid that millions of programmers waste millions of manyears on
compatibility with architectures that died dozens of years ago ;-).
Just like C99 is already said to require that integer 0 has all bits at
0 (section 6.2.6.2 of C99, citation without checking myself)

I just googled for architectures with NULL pointers or floating point
0.0 whose binary representation is not 0, but failed. Can anyone
provide examples?

Ciao,
Elmar

May 10 '06 #29
el***@cmbi.ru.n l said:
I just googled for architectures with NULL pointers or floating point
0.0 whose binary representation is not 0, but failed. Can anyone
provide examples?


Please read the FAQ in future to ensure that your question is not answered
there, before asking it here. For your reference:

<http://c-faq.com/null/machexamp.html>

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
May 10 '06 #30

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

Similar topics

23
22194
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
2471
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
17422
by: infobahn | last post by:
printf("%p\n", (void *)0); /* UB, or not? Please explain your answer. */
14
3021
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
16457
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
5670
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
9297
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
9735
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
8736
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...
1
7285
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
6556
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
5324
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
3828
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
3395
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
2697
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.