Why pointers to pointers used

Discussion in 'C Programming' started by ArifulHossain tuhin, Feb 28, 2012.

  1. I have used many OSS libraries which have a common structure like the following:

    They have init function which takes a pointer to pointer to the module data structure like this:
    module_init(struct module_ds ** p,.....);
    And usage example is like following:

    struct module_ds * p;
    p = NULL;
    module_init(&p,....);
    etc.

    Now i'm wondering why they do it this way? if i pass a plain pointer then it should be ok to declare a local pointer and allocate and initialize it. after initialization it can be returned by the fuction.

    but they always have a void type.

    Even if they remain "void" still it should be possible to allocate, and initialize a local pointer and assign it to a plain pointer comes as a argument?

    Or "local" pointer is key here?

    Thanks in advance.
     
    ArifulHossain tuhin, Feb 28, 2012
    #1
    1. Advertising

  2. ArifulHossain tuhin <> writes:

    > I have used many OSS libraries which have a common structure like the
    > following:
    >
    > They have init function which takes a pointer to pointer to the module
    > data structure like this:
    > module_init(struct module_ds ** p,.....);
    > And usage example is like following:
    >
    > struct module_ds * p;
    > p = NULL;
    > module_init(&p,....);
    > etc.
    >
    > Now i'm wondering why they do it this way? if i pass a plain pointer
    > then it should be ok to declare a local pointer and allocate and
    > initialize it. after initialization it can be returned by the fuction.


    Words may be getting in the way here! You do declare and allocate a
    local pointer, and there is no reason you can't initialise it (you
    assign to it, but you could have initialised it if you'd wanted to).

    There are several things you could mean. Maybe you are asking why you
    can't allocate a local struct module_ds object? That's usually because
    the library writer wants to keep you from messing up some important
    internal state. To day that, they will make struct module_ds
    incomplete so you can't declare one.

    Alternatively you might be asking why you can't allocate the storage
    that p should point to yourself. The answer to that may be the same.
    The full declaration of the struct may, deliberately, not be visible so
    you don't know how much storage to allocate, but it might simply be
    convenience. Why would you want to? It's simpler if the library
    provides a function to do it for you.

    One question is very clear: why is the function return type "void"? I
    don't know. That's almost always a mistake in my option. A function
    should only rarely want to waste the opportunity to return something
    helpful. I'd want:

    struct module_ds *p = module_init(...);

    But another option is to return an error value:

    struct module_ds *p;
    if (!module_init(&p, ...)) /* act on the failure */

    > but they always have a void type.


    I agree this is odd.

    > Even if they remain "void" still it should be possible to allocate,
    > and initialize a local pointer and assign it to a plain pointer comes
    > as a argument?


    Sorry, but I don't really follow what you mean here.

    > Or "local" pointer is key here?


    --
    Ben.
     
    Ben Bacarisse, Feb 28, 2012
    #2
    1. Advertising

  3. China Blue Sea <> writes:
    <snip>
    > I prefer the first with the general paradigm of
    > output = function(input)
    >
    > However some people take 'function' literally and want a function to
    > be free of external side effects. They use the general paradigm of
    >
    > function(input, &output)
    > or
    > function(&output, input)


    Surely it is the first (the one we both prefer) that stresses the pure
    functional nature of "function". I'd say that the latter two explicitly
    stress side effects.

    <snip>
    --
    Ben.
     
    Ben Bacarisse, Feb 28, 2012
    #3
  4. ArifulHossain tuhin

    Eric Sosman Guest

    On 2/28/2012 5:17 AM, ArifulHossain tuhin wrote:
    > [reformatted for line length]
    > I have used many OSS libraries which have a common structure like
    > the following:
    >
    > They have init function which takes a pointer to pointer to the
    > module data structure like this:
    > module_init(struct module_ds ** p,.....);
    > And usage example is like following:
    >
    > struct module_ds * p;
    > p = NULL;
    > module_init(&p,....);
    > etc.
    >
    > Now i'm wondering why they do it this way? if i pass a plain pointer
    > then it should be ok to declare a local pointer and allocate and
    > initialize it. after initialization it can be returned by the fuction.


    That's another style, also often seen:

    struct module_ds * p;
    p = module_init(...);

    > but they always have a void type.


    I'm not sure what you mean by "they." If it's "The module_init()
    functions," then I must disagree about the "always." Such functions
    often return a code describing success or failure, or a pointer to a
    freshly-initialized struct. They could, of course, return almost
    anything their designers dream up -- but in my experience it's usually
    one of these two. Initialization functions that return nothing are
    (again, in my experience) comparatively rare.

    > Even if they remain "void" still it should be possible to allocate,
    > and initialize a local pointer and assign it to a plain pointer comes
    > as a argument?


    No: A function cannot change its arguments. It can change its
    parameters -- the local variables that are initialized from the call's
    argument expressions -- and it can change things its arguments point
    to, but it cannot change the arguments. If module_init() allocated a
    new struct and set one of its parameters to point at that new struct,
    the effect on the caller would be precisely nil.

    > Or "local" pointer is key here?


    Again, I'm not sure what you're asking.

    One possible use case for the pointer-to-pointer style could be
    in a module that wanted to make "re-initialization" a no-op. The
    initialization function might look like

    void module_init(struct module_ds **p, /* other stuff */ ) {
    if (*p == NULL) {
    *p = malloc(sizeof **p);
    if (*p != NULL) {
    /* initialize the new struct */
    }
    }
    }

    That is, a new struct is allocated and initialized only if the
    pointed-to pointer is NULL; nothing happens if initialization has
    already occurred. This would allow

    struct module_ds *p = NULL;
    if (this) {
    module_init(&p,...);
    ...
    }
    if (that) {
    module_init(&p,...);
    ...
    }
    if (the_other) {
    module_init(&p,...);
    ...
    }

    In this use, the module is never initialized more than once, and
    is not initialized at all if this, that, and the_other are all false.
    You could achieve the same effect with other styles, but it might
    take more writing:

    struct module_ds *p = NULL;
    if (this) {
    if (p == NULL) p = module_init(...);
    ...
    }
    if (that) {
    if (p == NULL) p = module_init(...);
    ...
    }
    if (the_other) {
    if (p == NULL) p = module_init(...);
    ...
    }

    --
    Eric Sosman
    d
     
    Eric Sosman, Feb 28, 2012
    #4
  5. On Tuesday, February 28, 2012 7:19:54 PM UTC+6, Eric Sosman wrote:
    > On 2/28/2012 5:17 AM, ArifulHossain tuhin wrote:
    > > [reformatted for line length]
    > > I have used many OSS libraries which have a common structure like
    > > the following:
    > >
    > > They have init function which takes a pointer to pointer to the
    > > module data structure like this:
    > > module_init(struct module_ds ** p,.....);
    > > And usage example is like following:
    > >
    > > struct module_ds * p;
    > > p = NULL;
    > > module_init(&p,....);
    > > etc.
    > >
    > > Now i'm wondering why they do it this way? if i pass a plain pointer
    > > then it should be ok to declare a local pointer and allocate and
    > > initialize it. after initialization it can be returned by the fuction.

    >
    > That's another style, also often seen:
    >
    > struct module_ds * p;
    > p = module_init(...);
    >
    > > but they always have a void type.

    >
    > I'm not sure what you mean by "they." If it's "The module_init()
    > functions," then I must disagree about the "always." Such functions
    > often return a code describing success or failure, or a pointer to a
    > freshly-initialized struct. They could, of course, return almost
    > anything their designers dream up -- but in my experience it's usually
    > one of these two. Initialization functions that return nothing are
    > (again, in my experience) comparatively rare.
    >
    > > Even if they remain "void" still it should be possible to allocate,
    > > and initialize a local pointer and assign it to a plain pointer comes
    > > as a argument?

    >
    > No: A function cannot change its arguments. It can change its
    > parameters -- the local variables that are initialized from the call's
    > argument expressions -- and it can change things its arguments point
    > to, but it cannot change the arguments. If module_init() allocated a
    > new struct and set one of its parameters to point at that new struct,
    > the effect on the caller would be precisely nil.
    >



    That's where i'm missing the point.thank you.
    > > Or "local" pointer is key here?

    >
    > Again, I'm not sure what you're asking.
    >
    > One possible use case for the pointer-to-pointer style could be
    > in a module that wanted to make "re-initialization" a no-op. The
    > initialization function might look like
    >
    > void module_init(struct module_ds **p, /* other stuff */ ) {
    > if (*p == NULL) {
    > *p = malloc(sizeof **p);
    > if (*p != NULL) {
    > /* initialize the new struct */
    > }
    > }
    > }
    >
    > That is, a new struct is allocated and initialized only if the
    > pointed-to pointer is NULL; nothing happens if initialization has
    > already occurred. This would allow
    >
    > struct module_ds *p = NULL;
    > if (this) {
    > module_init(&p,...);
    > ...
    > }
    > if (that) {
    > module_init(&p,...);
    > ...
    > }
    > if (the_other) {
    > module_init(&p,...);
    > ...
    > }
    >
    > In this use, the module is never initialized more than once, and
    > is not initialized at all if this, that, and the_other are all false.
    > You could achieve the same effect with other styles, but it might
    > take more writing:
    >
    > struct module_ds *p = NULL;
    > if (this) {
    > if (p == NULL) p = module_init(...);
    > ...
    > }
    > if (that) {
    > if (p == NULL) p = module_init(...);
    > ...
    > }
    > if (the_other) {
    > if (p == NULL) p = module_init(...);
    > ...
    > }
    >


    That's seems more than reasonable.
    > --
    > Eric Sosman
    > d
     
    ArifulHossain tuhin, Feb 28, 2012
    #5
  6. ArifulHossain tuhin wrote:
    > I have used many OSS libraries which have a common structure like the following:
    >
    > They have init function which takes a pointer to pointer to the module data structure like this:
    > module_init(struct module_ds ** p,.....);
    > And usage example is like following:
    >
    > struct module_ds * p;
    > p = NULL;
    > module_init(&p,....);
    > etc.
    >


    Are you *sure* it is not:

    struct module_ds p;//!? no * here ?!
    module_init(&p,....);

    I think this is seen most often...
    Developers may choose this option to seperate *instantiation* from
    *initialisation*.

    So one _may_ create the structure in various ways (using malloc, as an
    auto var, or as global data...)

    It allows for more freedom.

    the call to module_init then just initializes content, but does not
    allocate anything.

    Certain OO purists may frown on that approach, as a pointer type can at
    least be set to NULL to signify invalid, while an uninitialized
    structure is more likely to cause all sorts trouble.

    > Now i'm wondering why they do it this way? if i pass a plain pointer then it should be ok to declare a local pointer and allocate and initialize it. after initialization it can be returned by the fuction.
    >

    It seems they they want to *write* your p pointer.
    probably something like:

    void module_init(struct module_ds **points_to_p,....);
    {
    *points_to_p=malloc(whatever);
    }

    > but they always have a void type.

    return type, I assume.
    Yes, you are right. If it was just about allocating, it would be easier
    to return it.
    >
    > Even if they remain "void" still it should be possible to allocate, and initialize a local pointer and assign it to a plain pointer comes as a argument?
    >
    > Or "local" pointer is key here?
    >

    parameters passed to functions get copied and live only inside that
    function. They are similar to auto variables, but initialized. Change
    does not propagate up the call tree.

    The 'plain pointer comes as an argument' will not be seen outside.

    > Thanks in advance.
     
    Johann Klammer, Feb 28, 2012
    #6
  7. ArifulHossain tuhin

    Nobody Guest

    On Tue, 28 Feb 2012 15:33:24 +0100, Johann Klammer wrote:

    >> struct module_ds * p;
    >> p = NULL;
    >> module_init(&p,....);
    >> etc.
    >>
    >>

    > Are you *sure* it is not:
    >
    > struct module_ds p;//!? no * here ?!
    > module_init(&p,....);
    >
    > I think this is seen most often...


    The advantage of the former is that it allows "struct module_ds" to be an
    opaque type. Client code doesn't need to know the details of the
    structure, not even its size. This can be useful if the library has
    compile-time options which affect the size or layout of the structure, as
    it doesn't require the client to be re-compiled to use a different
    configuration of the library.
     
    Nobody, Feb 28, 2012
    #7
  8. ArifulHossain tuhin

    tom st denis Guest

    On Feb 28, 6:06 am, China Blue Sea <> wrote:
    > > Now i'm wondering why they do it this way? if i pass a plain pointer then it
    > > should be ok to declare a local pointer and allocate and initialize it.after
    > > initialization it can be returned by the fuction.

    >
    > If you want to return (Type*), you have to either have the function return it
    >     Type *function(...);
    >     Type *var = function(...);
    >
    > assign it to an output parameter
    >     void function(Type **var,...);
    >     Type *var; function(&var, ...);
    >
    > or assign it to/through a global variable
    >     extern Type *functionvalue;
    >     voidfunction(...);
    >     function(...);
    >     Type *var = functionvalue;
    >
    > I prefer the first with the general paradigm of
    >     output = function(input)
    >
    > However some people take 'function' literally and want a function to be free of
    > external side effects. They use the general paradigm of
    >     function(input, &output)


    The advantage of something like

    int function(struct foo **param);

    Is that you can return an error code AND initialize a pointer.

    It's better when you have things like

    int function(void **foo);

    So that the data type is opaque to the user. This is handy in a
    plugin scenario (like in bignum math where you might use different
    structures for integers depending on the provider).

    Tom
     
    tom st denis, Feb 28, 2012
    #8
  9. ArifulHossain tuhin

    Philip Lantz Guest

    Ben Bacarisse wrote:
    > China Blue Sea <> writes:
    > > I prefer the first with the general paradigm of
    > > output = function(input)
    > >
    > > However some people take 'function' literally and want a function to
    > > be free of external side effects. They use the general paradigm of
    > >
    > > function(input, &output)
    > > or
    > > function(&output, input)

    >
    > Surely it is the first (the one we both prefer) that stresses the pure
    > functional nature of "function". I'd say that the latter two explicitly
    > stress side effects.


    I'm sure that when he said "some people take 'function' literally", what
    he was referring to is that most functions in most C programs are not
    free of external side effects, and that some people take that to mean
    that they must not return a value, and any result must be returned via a
    pointer parameter.
     
    Philip Lantz, Feb 29, 2012
    #9
  10. ArifulHossain tuhin

    James Kuyper Guest

    On 02/29/2012 04:06 AM, Philip Lantz wrote:
    > Ben Bacarisse wrote:
    >> China Blue Sea <> writes:
    >>> I prefer the first with the general paradigm of
    >>> output = function(input)
    >>>
    >>> However some people take 'function' literally and want a function to
    >>> be free of external side effects. They use the general paradigm of
    >>>
    >>> function(input, &output)
    >>> or
    >>> function(&output, input)

    >>
    >> Surely it is the first (the one we both prefer) that stresses the pure
    >> functional nature of "function". I'd say that the latter two explicitly
    >> stress side effects.

    >
    > I'm sure that when he said "some people take 'function' literally", what
    > he was referring to is that most functions in most C programs are not
    > free of external side effects, and that some people take that to mean
    > that they must not return a value, and any result must be returned via a
    > pointer parameter.


    Returning a value through a pointer parameter requires use of assignment
    to the pointed-at object, from within the called function, which is a
    side-effect that is external to the called function. Returning a value
    does not have side effects. The returned value may end up being assigned
    somewhere, but that assignment would occur inside the calling function,
    not the called one.
    --
    James Kuyper
     
    James Kuyper, Feb 29, 2012
    #10
  11. On Feb 29, 9:06 am, Philip Lantz <> wrote:
    > Ben Bacarisse wrote:
    > > China Blue Sea <> writes:


    > > > I prefer the first with the general paradigm of
    > > >     output = function(input)

    >
    > > > However some people take 'function' literally and want a function to
    > > > be free of external side effects. They use the general paradigm of

    >
    > > >  function(input, &output)
    > > > or
    > > >  function(&output, input)

    >
    > > Surely it is the first (the one we both prefer) that stresses the pure
    > > functional nature of "function".  I'd say that the latter two explicitly
    > > stress side effects.

    >
    > I'm sure that when he said "some people take 'function' literally", what
    > he was referring to is that most functions in most C programs are not
    > free of external side effects, and that some people take that to mean
    > that they must not return a value, and any result must be returned via a
    > pointer parameter.


    they have their logic strangly inverted then!

    In order to avoid a non-existent side effect they introduce a real
    side effect!

    To be honest I regard

    getCurrentLargeObject (&currentLargeObject);

    as morally a function. I'm only using a pointer because C is rubbish
    at assigning large objects.

    I also might use a ptr if I want to use the return value to signal an
    error

    rv = findMatchingTherblib (&therblig, matchp);
     
    Nick Keighley, Feb 29, 2012
    #11
  12. On Feb 29, 12:01 pm, James Kuyper <> wrote:
    > On 02/29/2012 04:06 AM, Philip Lantz wrote:
    >
    >
    >
    >
    >
    > > Ben Bacarisse wrote:
    > >> China Blue Sea <> writes:
    > >>> I prefer the first with the general paradigm of
    > >>>     output = function(input)

    >
    > >>> However some people take 'function' literally and want a function to
    > >>> be free of external side effects. They use the general paradigm of

    >
    > >>>  function(input, &output)
    > >>> or
    > >>>  function(&output, input)

    >
    > >> Surely it is the first (the one we both prefer) that stresses the pure
    > >> functional nature of "function".  I'd say that the latter two explicitly
    > >> stress side effects.

    >
    > > I'm sure that when he said "some people take 'function' literally", what
    > > he was referring to is that most functions in most C programs are not
    > > free of external side effects, and that some people take that to mean
    > > that they must not return a value, and any result must be returned via a
    > > pointer parameter.

    >
    > Returning a value through a pointer parameter requires use of assignment
    > to the pointed-at object, from within the called function, which is a
    > side-effect that is external to the called function. Returning a value
    > does not have side effects. The returned value may end up being assigned
    > somewhere, but that assignment would occur inside the calling function,
    > not the called one.
    > --
    > James Kuyper- Hide quoted text -
    >
    > - Show quoted text -
     
    Nick Keighley, Feb 29, 2012
    #12
  13. ArifulHossain tuhin

    Philip Lantz Guest

    Nick Keighley wrote:
    >
    > On Feb 29, 9:06 am, Philip Lantz <> wrote:
    > > Ben Bacarisse wrote:
    > > > China Blue Sea <> writes:

    >
    > > > > I prefer the first with the general paradigm of
    > > > >     output = function(input)

    > >
    > > > > However some people take 'function' literally and want a function to
    > > > > be free of external side effects. They use the general paradigm of

    > >
    > > > >  function(input, &output)
    > > > > or
    > > > >  function(&output, input)

    > >
    > > > Surely it is the first (the one we both prefer) that stresses the pure
    > > > functional nature of "function".  I'd say that the latter two explicitly
    > > > stress side effects.

    > >
    > > I'm sure that when he said "some people take 'function' literally", what
    > > he was referring to is that most functions in most C programs are not
    > > free of external side effects, and that some people take that to mean
    > > that they must not return a value, and any result must be returned via a
    > > pointer parameter.

    >
    > they have their logic strangly inverted then!
    >
    > In order to avoid a non-existent side effect they introduce a real
    > side effect!


    No, I'm sorry, both you and James didn't get what I was trying to say.
    My point was, for people that feel that way, if they have a C function
    that *does* have side effects (as most do, in my experience), then they
    feel that the C function is not a "function" (in their terms), and so it
    must not return a value. If the function doesn't have any other side
    effects, I'm sure that they would not introduce one, but rather would be
    happy to return a value in the normal way.

    I don't happen to agree with this constraint, but it's not totally
    nonsensical. (Totally pointless, I would say, but not nonsensical.)

    Does that clear things up?
     
    Philip Lantz, Mar 1, 2012
    #13
  14. ArifulHossain tuhin

    James Kuyper Guest

    On 03/01/2012 04:06 AM, Philip Lantz wrote:
    ....
    > I don't happen to agree with this constraint, but it's not totally
    > nonsensical. (Totally pointless, I would say, but not nonsensical.)
    >
    > Does that clear things up?


    Not really, but that's generally what happens when someone tries to
    describe a point of view that they don't agree with. I don't think it's
    worth discussing any further.
    --
    James Kuyper
     
    James Kuyper, Mar 1, 2012
    #14
  15. ArifulHossain tuhin

    Joe keane Guest

    In article <>,
    Nick Keighley <> wrote:
    >To be honest I regard
    >
    > getCurrentLargeObject (&currentLargeObject);
    >
    >as morally a function. I'm only using a pointer because C is rubbish
    >at assigning large objects.


    Did you time it? Very likely the compiler will generate the exact same
    code no matter which form you use.
     
    Joe keane, Mar 1, 2012
    #15
  16. ArifulHossain tuhin

    Willem Guest

    Joe keane wrote:
    ) In article <>,
    ) Nick Keighley <> wrote:
    )>To be honest I regard
    )>
    )> getCurrentLargeObject (&currentLargeObject);
    )>
    )>as morally a function. I'm only using a pointer because C is rubbish
    )>at assigning large objects.
    )
    ) Did you time it? Very likely the compiler will generate the exact same
    ) code no matter which form you use.

    Only if both functions are in the same unit, or if the compiler can inline
    functions across units.


    SaSW, Willem
    --
    Disclaimer: I am in no way responsible for any of the statements
    made in the above text. For all I know I might be
    drugged or something..
    No I'm not paranoid. You all think I'm paranoid, don't you !
    #EOT
     
    Willem, Mar 1, 2012
    #16
  17. On 29-Feb-12 06:52, Nick Keighley wrote:
    > To be honest I regard
    >
    > getCurrentLargeObject (&currentLargeObject);
    >
    > as morally a function. I'm only using a pointer because C is rubbish
    > at assigning large objects.


    It's my experience that "assigning large objects" is rarely the correct
    solution anyway; in most cases, it's much easier to work with pointers
    to those objects rather than the objects themselves.

    You can also return a pointer-to-const-object if the caller should be
    required to make a copy (via memcpy() or the like) of the object rather
    than modify the pointed-to one that is returned.

    > I also might use a ptr if I want to use the return value to signal an
    > error
    >
    > rv = findMatchingTherblib (&therblig, matchp);


    That's one variant of the hack around C's poor support for returning two
    values, which has led to all sorts of problems over the years.

    Still, if you return a pointer-to-therblib (see above), then returning
    NULL is an obvious error signal. It's only complicated if you need to
    return more than one type of error--which I suspect is where errno came
    from.

    S

    --
    Stephen Sprunk "God does not play dice." --Albert Einstein
    CCIE #3723 "God is an inveterate gambler, and He throws the
    K5SSS dice at every possible opportunity." --Stephen Hawking
     
    Stephen Sprunk, Mar 1, 2012
    #17
    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. Anand Sagar
    Replies:
    2
    Views:
    1,983
  2. Mr. SweatyFinger

    why why why why why

    Mr. SweatyFinger, Nov 28, 2006, in forum: ASP .Net
    Replies:
    4
    Views:
    906
    Mark Rae
    Dec 21, 2006
  3. Mr. SweatyFinger
    Replies:
    2
    Views:
    1,978
    Smokey Grindel
    Dec 2, 2006
  4. Casey Hawthorne
    Replies:
    1
    Views:
    718
    Arne Vajhøj
    Mar 18, 2009
  5. cerr

    pointers, pointers, pointers...

    cerr, Apr 7, 2011, in forum: C Programming
    Replies:
    12
    Views:
    680
Loading...

Share This Page