void **ptr?

2 views
Skip to first unread message

Lewis Berman

unread,
Mar 3, 1992, 2:06:21 PM3/3/92
to
What does void **ptr; mean, as in:

int mapFB(dev, st, num, off, size, ptr)
int dev;
short sB;
short num;
long off, size;
void **ptr;


In the routine that calls this function how do I declare the
the variable that will be passed in and referenced in mapFB as 'ptr'?


Thanks,

Lew Berman

Small Systems Solutions

unread,
Mar 3, 1992, 6:24:41 PM3/3/92
to
In article <1992Mar3.1...@nlm.nih.gov>

ber...@ursp03.nlm.nih.gov (Lewis Berman) writes:
>What does void **ptr; mean, as in:
>
>int mapFB(dev, st, num, off, size, ptr)
> void **ptr;
>

ptr is a reference to a void pointer (the address of a void pointer).

if the reference you wish to pass is, say

int *bozo, x;

you'd say

x = mapFB(dev, st, num, off, size, &(void *) bozo);
--

Small Systems Solutions
1563 Solano Avenue, Suite 123
Berkeley, CA 94707-2116 s...@netcom.com

Small Systems Solutions

unread,
Mar 3, 1992, 7:13:12 PM3/3/92
to
In article <_lthn...@netcom.com>

s...@netcom.com (Small Systems Solutions) writes:
>
> x = mapFB(dev, st, num, off, size, &(void *) bozo);

Do as I *mean*, not as I *say*, as always! That little bit of
brain damage (I've been awake too long!) will get a warning
or error from any competent compiler: "not an lvalue"

I *meant*

&(void *) bozo

to be

(void **) &bozo

Stuart Hall

unread,
Mar 4, 1992, 10:34:47 AM3/4/92
to
>/ col:comp.lang.c / ber...@ursp03.nlm.nih.gov (Lewis Berman) / 12:06 pm Mar 3, 1992 /
>----------

void **ptr; /* pointer to a pointer to void */

Pointer to void is often used as a variable that can be caste to a pointer to
anything else, so it's useful for things like linked lists and similar structs.

A pointer to a pointer to void is passed to the function mapFB possibly so that
mapFB may modify the value of the pointer to void (the address that the pointer
to void points to).

As for calling a function, how about...
******************
static char str1[50];
static char str2[50];

main()
{
void *genericPtr;

sprintf(str1,"here's some data");
sprintf(str2,"there's some data");

/* I think this is how one might use this */
genericPtr = (void *)str1;

changeWherePtrPoints(&genericPtr);
}

void changeWherePtrPoints(void **ptr)
{
/* dereferenced ptr to ptr to void = ptr to void */
*ptr = (void *)str2;
}

**************
stu

Steve Summit

unread,
Mar 4, 1992, 12:57:47 PM3/4/92
to
In article <1992Mar3.1...@nlm.nih.gov> ber...@ursp03.nlm.nih.gov (Lewis Berman) writes:
> int mapFB(dev, st, num, off, size, ptr)
> int dev;
> short sB;
> short num;
> long off, size;
> void **ptr;
>
> In the routine that calls this function how do I declare the
> the variable that will be passed in and referenced in mapFB as 'ptr'?

Quite simply. Void ** means "pointer to pointer to void." So
declare a void * variable:

void *myptr;

and pass a pointer to it to mapFB:

mapFB(..., &myptr)

(Beware, though: this is a potentially misleading answer.
Keep reading, or skip forward 144 lines to the end.)

If this seems at all mysterious, consider an analogous case, with
one less level of indirection. If we have a function which
accepts a pointer to an int:

f(ip)
int *ip;

we declare an int variable, and pass a pointer to it:

int i;
f(&i);

There is one thing to be aware of when dealing with void **'s.
Although void * is a "generic pointer," and in a sense converts
itself automatically to and from the other pointer types you
might be using, void ** is *not* a generic pointer-to-pointer.

Since void * is a generic pointer, it is useful for routines like
malloc. We can say

extern void *malloc(); /* <stdlib.h> normally does this for us */
int *ip = malloc(sizeof(int));

and, with or without a cast, malloc's return value is correctly
converted into a pointer-to-int. (Under pre-ANSI compilers, an
explicit cast is typically required, at least to silence
warnings.)

However, void ** won't do what you might want it to do, even with
casts. If you want a subroutine to accept and return generic
pointers indirectly through a pointer (rather than by-value
arguments and direct return values), void ** won't cut it. You
might try

int *ip = malloc(sizeof(int));
myrealloc((void **)&ip, 2 * sizeof(int));

where myrealloc is declared as

myrealloc(ptr, newsize)
void **ptr;
size_t newsize;

and you might be lulled into a false sense of security, because a
function like this will happen to work on many machines, but it
is not portable.

Why not? By computing (void **)&ip, and passing it to the
routine myrealloc which accepts a void **, the code above
arranges that myrealloc treat the pointer-to-int value ip as if
it were a pointer-to-void. There is no guarantee in Standard C
(or in "classic" K&R C, for that matter), that two pointer types,
such as pointer-to-int and pointer-to-void, have the same size or
representation [footnotes 1, 2].

But, you ask, if pointer-to-int and pointer-to-void might not
have the same size or representation, how can

int *ip = malloc(sizeof(int));

work? The answer is that an implicit cast is inserted (due to
explicit language in X3.159, section 3.3.16.1, p. 54, lines
26-28), so that the effect is the same as if we had written

int *ip = (int *)malloc(sizeof(int));

Remember, too, that cast operators such as (int *) are more than
just idle handwavings to shut lint up; they can induce changes in
the value being cast. On a system for which the types
pointer-to-int and pointer-to-void have different sizes or
representations, the (int *) cast (whether implicit or explicit)
would take care of converting the latter to the former.

The conversion which is implied by

int *ip = malloc(sizeof(int));

is therefore conceptually quite similar to that found in

#include <math.h>
int i = sqrt(25.);

sqrt returns a double, but it is implicitly converted to an int
when it is assigned to a variable of type int.

The point which I am gradually leading up to is that these
implicit conversions typically take place only in assignments
[footnote 3]. In the context of an assignment, the compiler has
all of the information in hand (i.e. both the source and
destination type), and can emit code to implement a single,
well-defined conversion operation, if necessary. Contrast this
with the situation in

int *ip = malloc(sizeof(int));
myrealloc((void **)&ip, 2 * sizeof(int));

Here, the compiler may arrange to convert a pointer-to-pointer-to-int
value into a pointer-to-pointer-to-void, but the underlying
pointer-to-int value (ip, the thing being pointed to) remains
untouched (it's still sitting somewhere in memory; it just had
its address taken with &ip). Down within myrealloc, we might
find code like

*ptr = realloc(*ptr, size);

ptr, remember, is one of myrealloc's arguments, and is declared as

void **ptr;

Therefore, the subexpression *ptr has type pointer-to-void, and
the compiler will assume that ptr points to a value of that type.
myrealloc may be compiled separately from the code calling it, so
the compiler has no way of knowing that ptr might really point to
a pointer-to-int. (After all, if ptr pointed to a pointer-to-int,
it would be declared as int **ptr.)

Lest anyone think I am making a mountain out of a molehill here,
in article <_lthn...@netcom.com>, s...@netcom.com (Small Systems


Solutions) writes:
> if the reference you wish to pass is, say
>
> int *bozo, x;
>
> you'd say
>

> x = mapFB(dev, st, num, off, size, &(void *) bozo);

and then corrects itself in article <jmthw=_s...@netcom.com>:


> Do as I *mean*, not as I *say*, as always! That little bit of
> brain damage (I've been awake too long!) will get a warning
> or error from any competent compiler: "not an lvalue"
>
> I *meant*

> ...
> (void **) &bozo

It's true that the subexpression &(void *)bozo will elicit an
error like "not an lvalue" (and since we've all read question
2.11 in this group's FAQ list, we all know why), but (void **)&bozo
isn't right either, for the reasons I've somewhat long-windedly
discussed above.

When you've got a routine (such as mapFB in Lewis's original
question, or my hypothetical myrealloc) that plays with a pointer
through a void ** reference, and you want to use it to play with
a pointer to other than void (or char), you'll have to introduce
a temporary variable, like this:

int *ip = malloc(sizeof(int));
void *tmp = ip;
myrealloc(&tmp, 2 * sizeof(int));
ip = tmp;

Since we've added two assignment statements, the compiler knows
where to place the implicit (but possibly significant) casts
between pointer-to-int and pointer-to-void. Since we've defined
a little location which can hold a void * value (namely the
variable tmp), the myrealloc routine can correctly play with a
void * value when it dereferences its ptr argument.

If you've stuck with me this long, you should understand why void **
is essentially useless as a by-reference generic pointer type,
because it can't be used as such without annoying explicit
temporary variables. If a subroutine must accept or return
generic pointers, it should accept them with conventional void *
by-value parameters, and return them with a conventional void *
return value.

Steve Summit
s...@adam.mit.edu

Footnote 1. Standard C does guarantee that the types
pointer-to-char and pointer-to-void have the same size and
representation, but this is not true for arbitrary types
pointer-to-X and pointer-to-Y, even if one of X or Y is "void."
(There are also other guarantees, having to do with qualified
and unqualified versions of compatible types.)

Footnote 2. Of course, K&R C didn't have the types void or
pointer-to-void.

Footnote 3. Implicit conversions also take place during argument
passing with a prototype in scope, but argument passing with a
prototype in scope is defined in terms of assignment semantics.

er...@pencom.com

unread,
Mar 4, 1992, 12:04:24 PM3/4/92
to
In article <1992Mar3.1...@nlm.nih.gov> ber...@ursp03.nlm.nih.gov (Lewis
Berman) writes:

void **ptr is a pointer to a pointer.

void *ptr ;

call with :
mapFB(dev,str,num,off,size,&ptr) ;

Use it this way iff mapFB wants to return somehting in ptr.


If mapFB really wants an array of void *'s you might have to do somehting
like :

void *ptrs[] = {
....
} ;

mapFB(dev,str,num,off,size,ptrs) ;

Wayne Throop

unread,
Mar 17, 1992, 10:50:14 PM3/17/92
to
> s...@adam.mit.edu (Steve Summit)

> There is one thing to be aware of when dealing with void **'s.
> Although void * is a "generic pointer," and in a sense converts
> itself automatically to and from the other pointer types you
> might be using, void ** is *not* a generic pointer-to-pointer.

Steve's advice is sage, as usual. It is very important to keep in mind
that a pointer to a generic pointer is not, itself, generic.

Not directly related, but of perhaps peripheral interest is the ocasion
I had to surprise myself by using a (void ***) type in an interface.
Consider this interface to hash table routines:

typedef struct ht_table_s ht_table_t;
typedef struct ht_node_s *ht_place_t;

ht_table_t * ht_create (int size);
void * ht_find (ht_table_t*ref, char*key );
int ht_find_ref (ht_table_t*ref, char*key,
char***keyref, void***valueref );
int ht_next (ht_table_t*ref, char**key, void**value,
ht_place_t*place );
void ht_delete (ht_table_t*ref);

The obvious routines, creation (ht_create), lookup (ht_find), iteration
over members (ht_next), deletion (ht_delete). (Well, the iteration
routine is a little non-obvious, but I'll let that be for now...)

But rather than a raft of "set value" routines and the like, I decided
to have this level of hash table always map character strings to (void*).
The ht_find_ref has three conceptual return values. It returns a
C-boolean indicator as to whether the entry pre-existed. It also
"returns" the addresses of the pointer pair that is related by the hash
lookup. (As a final complication, if the void***valueref argument has
the value ((void***)0), a new entry is not made, otherwise it IS made
and the int return value is always (1).)

So, (void*) (and (char*)) because that's what the hash table maps
from/to, (void**) (and (char**))) because the address (a reference to)
the pointers stored in the table must be "returned", and finally
(void***) (and (char***)))) because we are "returning" these by
passing in the address of a (void**) (and (char***))).

Yes, it made me ill, too. But is has proven very useful at this
level of utility routine, much like the "extra" level of indirection
in the arguments to the qsort(3) compare routine is useful despite
giving people fits when using them. (I had experimented around,
returning a struct with the three values, or passing in a reference
to such a struct, but this arrangement has proven most useful.)

Hmmmmmm. I wonder if this ought to be in alt.hack or somesuch?
--
Wayne Throop ...!mcnc!dg-rtp!sheol!throopw

Reply all
Reply to author
Forward
0 new messages