Pointer to Pointer Paramters (aka void **)

Discussion in 'C Programming' started by bwaichu@yahoo.com, Aug 7, 2006.

  1. Guest

    In some APIs, we see char ** or void ** as a parameter. I never
    distinguished between declaring a variable as char **x and passing x as
    the parameter from declaring a variable char *x and passing &x. I also
    consider the two the same. And since I debug everything I write, I
    also saw know difference while debugging. I have also used them pass
    data in and out of functions as well.

    Now, I was pointed out today that there is a difference. I was even
    pointed out this is addressed in the standards.

    What I would like to know is what is the proper way to handle APIs that
    have functions with void ** parameters.

    One interesting item brought up is that I might end up overwriting a
    previously declared variable.
    Anyway, I hope to learn more.

    Brian
    , Aug 7, 2006
    #1
    1. Advertising

  2. wrote:
    > In some APIs, we see char ** or void ** as a parameter. I never
    > distinguished between declaring a variable as char **x and passing x as
    > the parameter from declaring a variable char *x and passing &x. I also
    > consider the two the same. And since I debug everything I write, I
    > also saw know difference while debugging. I have also used them pass
    > data in and out of functions as well.
    >
    > Now, I was pointed out today that there is a difference. I was even
    > pointed out this is addressed in the standards.
    >
    > What I would like to know is what is the proper way to handle APIs that
    > have functions with void ** parameters.
    >
    > One interesting item brought up is that I might end up overwriting a
    > previously declared variable.
    > Anyway, I hope to learn more.


    It's not at all clear what you're confused about. Parameters to
    functions are defined to be of certain types, and you must pass
    parameters of those types (or ones which get automatically converted to
    those types) when you call the function. If the parameter type is 'int'
    you must pass an int; if it's 'struct frooble *' you must pass a
    pointer to a struct frooble; if it's 'void **' you must pass a pointer
    to a pointer to void.
    J. J. Farrell, Aug 7, 2006
    #2
    1. Advertising

  3. Snis Pilbor Guest

    wrote:
    > In some APIs, we see char ** or void ** as a parameter. I never
    > distinguished between declaring a variable as char **x and passing x as
    > the parameter from declaring a variable char *x and passing &x. I also
    > consider the two the same. And since I debug everything I write, I
    > also saw know difference while debugging. I have also used them pass
    > data in and out of functions as well.
    >


    In my experience any time a function takes char ** or void **
    arguments, it is for one of the following reasons:
    1. the function depends not on a single char * or void * but on a
    contiguous block of such, or
    2. the function is meant to destructively alter the char * or void *,
    for example a swap function

    If the first usage is what is going on here, then that usage would not
    realize its full potential unless some calling function declares the
    variable as char **x and passes x. If in every single case, the
    calling function declares the variable as char *x and passes &x, well,
    then the fancy contiguous block code is kind of pointless (or should I
    say "pointerless") since it only actually ever receives "contiguous
    blocks" of size 1!

    Nonetheless, I agree with you that there is no real difference.

    Snis Pilbor
    Snis Pilbor, Aug 7, 2006
    #3
  4. Snis Pilbor Guest

    Snis Pilbor wrote:
    > wrote:
    > > In some APIs, we see char ** or void ** as a parameter. I never
    > > distinguished between declaring a variable as char **x and passing x as
    > > the parameter from declaring a variable char *x and passing &x. I also
    > > consider the two the same. And since I debug everything I write, I
    > > also saw know difference while debugging. I have also used them pass
    > > data in and out of functions as well.
    > >

    >
    > In my experience any time a function takes char ** or void **
    > arguments, it is for one of the following reasons:
    > 1. the function depends not on a single char * or void * but on a
    > contiguous block of such, or
    > 2. the function is meant to destructively alter the char * or void *,
    > for example a swap function
    >
    > If the first usage is what is going on here, then that usage would not
    > realize its full potential unless some calling function declares the
    > variable as char **x and passes x. If in every single case, the
    > calling function declares the variable as char *x and passes &x, well,
    > then the fancy contiguous block code is kind of pointless (or should I
    > say "pointerless") since it only actually ever receives "contiguous
    > blocks" of size 1!
    >
    > Nonetheless, I agree with you that there is no real difference.
    >
    > Snis Pilbor


    Thinking about this a little more, I realized, you could in theory use
    a void * to store the address to a whole contiguous buffer of void *'s.
    This would probably be quite confusing, though I can see how in some
    very highly obscure cases it could actually be indispensible. So in
    the case of void, there is actually a third option, which is for the
    caller to pass (void **) x, as so:

    void **buf;
    void *ptr;

    buf = malloc( 100 * sizeof( void * ) );
    ptr = buf;
    nullify_pointer_array( (void **) ptr, 100 );

    I wonder if such scandalous code as this would ever actually be useful,
    or if it is just a funny example. (Of course in the above example,
    "nullify_pointer_array(buf,100)" would do the same thing and be 100
    times clearer)

    S.P.
    Snis Pilbor, Aug 7, 2006
    #4
  5. said:

    > In some APIs, we see char ** or void ** as a parameter. I never
    > distinguished between declaring a variable as char **x and passing x as
    > the parameter from declaring a variable char *x and passing &x. I also
    > consider the two the same.


    Clearly, they aren't.

    char *x;
    char **y;
    strtod(s, &x); /* &x is the address of an object of type char *. */
    strtod(s, y); /* y is an indeterminate value */

    > And since I debug everything I write, I
    > also saw know difference while debugging.


    You have now learned - I hope - that debugging is not sufficient. You have
    to know what you're doing, too.

    <snip>

    > What I would like to know is what is the proper way to handle APIs that
    > have functions with void ** parameters.


    It depends on what those functions are trying to do. Knowing the prototype
    for a function is insufficient. You have to understand the semantics of
    that function, too.

    In the case of strtod, what it is trying to tell you is the address of a
    character - the first character it couldn't convert. If it were written
    like this: char *strtod(char *s); that would be an easier way for it to
    tell you that address, but then it wouldn't be able to return the double!
    And it can't just be written like this: double strtod(char *s, char
    *endptr); because if it were, it wouldn't matter how much strtod tried to
    change the value of endptr - it would have no effect whatsoever on the char
    * that you passed in! That's because C is a pass-by-value language, always.
    And so strtod's designer(s) went for the next option, which is to take the
    address of a char * object. Why? So that they can store into that char *
    object the address of the first character they couldn't convert. Now, for
    that to be a legitimate thing to do, that object must ***exist***! And
    that's why it's insufficient to use char **endptr; What you /could/ do is
    this:

    char *endptr;
    char **intermediatevalue = &endptr;
    double d = strtod(s, intermediatevalue);

    and endptr now points to the first unconverted character. That code would
    have just the same validity, because now the value you are passing to
    strtod really is the address of a valid object.

    > One interesting item brought up is that I might end up overwriting a
    > previously declared variable.


    The C Standard does not place limits on the possible outcomes of undefined
    behaviour.

    --
    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)
    Richard Heathfield, Aug 7, 2006
    #5
  6. Snis Pilbor Guest

    Richard Heathfield wrote:
    > said:
    >
    > > In some APIs, we see char ** or void ** as a parameter. I never
    > > distinguished between declaring a variable as char **x and passing x as
    > > the parameter from declaring a variable char *x and passing &x. I also
    > > consider the two the same.

    >
    > Clearly, they aren't.
    >
    > char *x;
    > char **y;
    > strtod(s, &x); /* &x is the address of an object of type char *. */
    > strtod(s, y); /* y is an indeterminate value */
    >


    I think when the OP said "declaring a variable as char **x and passing
    x", it was implicitly assumed x would be initialized before being used.
    I'm nitpicking here because I wouldn't want someone to be confused by
    your post and think, "never declare char **y, it's automatically a Bad
    Thing".

    char *x;
    char **y;

    y = malloc( 50 * sizeof( char * ) );

    nullify_char_pointers( y, 50 );
    nullify_char_pointers( &x, 1 );

    Here the fact we call nullify_char_pointers on &x is silly since we
    could've just said "x=NULL", but the point is besides silliness there's
    no difference between the two calls.

    The OP was particularly concerned about passing with &x (where x is
    char *) being more potentially destructive than passing with x (where x
    is char **). At first I thought this was indeed a concern, but
    reflecting on it, they are equally safe despite intuition. In order
    for the function to act destructively, it would have to act on the
    dereferenced input. If it does this, it will act destructively
    regardless of how the input is passed, and if not, then not. I think.

    S.P. =)
    Snis Pilbor, Aug 7, 2006
    #6
  7. Snis Pilbor said:

    >
    > Richard Heathfield wrote:
    >> said:
    >>
    >> > In some APIs, we see char ** or void ** as a parameter. I never
    >> > distinguished between declaring a variable as char **x and passing x as
    >> > the parameter from declaring a variable char *x and passing &x. I also
    >> > consider the two the same.

    >>
    >> Clearly, they aren't.
    >>
    >> char *x;
    >> char **y;
    >> strtod(s, &x); /* &x is the address of an object of type char *. */
    >> strtod(s, y); /* y is an indeterminate value */
    >>

    >
    > I think when the OP said "declaring a variable as char **x and passing
    > x", it was implicitly assumed x would be initialized before being used.


    You think so? Then you might want to check out the original code he posted
    that prompted this discussion:

    Message-ID: <>

    --
    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)
    Richard Heathfield, Aug 7, 2006
    #7
  8. Guest

    Richard Heathfield wrote:

    > You think so? Then you might want to check out the original code he posted
    > that prompted this discussion:
    >
    > Message-ID: <>


    And he was completely right. I overlooked my approach thinking both of
    the approaches were valid, but both approaches are clearly not valid.
    But the process for me to understand that took some doing. The program
    compiled fine. The program "worked". And my debug session worked out
    fine. But what I was doing was wrong. The compiler did not even warn
    me until I turned on an additional flag per the suggestion of one of
    the folks in that thread. And that flag was an optimization flag, not
    even warning related. This was a tough item to learn because
    everything appeared to be working fine.

    Thanks Richard. Sorry about my difficulty in learning this item.
    Everything appeared to work and that made the learning process more
    difficult.
    , Aug 7, 2006
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Ollej Reemt
    Replies:
    7
    Views:
    494
    Jack Klein
    Apr 22, 2005
  2. Stig Brautaset

    `void **' revisited: void *pop(void **root)

    Stig Brautaset, Oct 25, 2003, in forum: C Programming
    Replies:
    15
    Views:
    767
    The Real OS/2 Guy
    Oct 28, 2003
  3. Replies:
    5
    Views:
    806
    S.Tobias
    Jul 22, 2005
  4. Hakirato
    Replies:
    4
    Views:
    853
    Alf P. Steinbach
    Oct 5, 2006
  5. Replies:
    1
    Views:
    384
    Victor Bazarov
    May 23, 2007
Loading...

Share This Page