Casting const void * into void *

Discussion in 'C Programming' started by Enrico `Trippo' Porreca, May 31, 2004.

  1. Given:

    typedef struct Node Node;
    struct Node {
    void *obj;
    Node *next;
    };

    typedef struct Stack Stack;
    struct Stack {
    Node *top;
    };

    ....is the following a conforming C function (I am particularly worried
    about the casts), since I'm not modifying the obj parameter?

    void *stack_push(Stack *s, const void *obj)
    {
    Node *n;

    assert(s != NULL);
    assert(obj != NULL);
    n = malloc(sizeof *n);
    if (n == NULL)
    return NULL; /* push failed */
    n->obj = (void *) obj;
    n->next = s->top;
    s->top = n;
    return (void *) obj; /* successful push */
    }

    I can't make the obj field of struct Node a const void *, since the user
    will probably free it by the "destructor" (I can't free a const void *,
    right?):

    void stack_destroy(Stack *s, void (*destroy)(void *))
    {
    Node *n, *tmp;

    assert(s != NULL);
    n = s->top;
    while (n != NULL) {
    tmp = n->next;
    if (destroy != NULL)
    destroy(n->obj); /* probably destroy == free */
    free(n);
    n = tmp;
    }
    free(s);
    }
     
    Enrico `Trippo' Porreca, May 31, 2004
    #1
    1. Advertising

  2. "Enrico `Trippo' Porreca" <> wrote in message news:...
    > Given:
    >
    > typedef struct Node Node;
    > struct Node {
    > void *obj;
    > Node *next;
    > };
    >
    > typedef struct Stack Stack;
    > struct Stack {
    > Node *top;
    > };
    >
    > ...is the following a conforming C function (I am particularly worried
    > about the casts), since I'm not modifying the obj parameter?


    If you are not modifying the obj parameter, then you could have used:
    void *stack_push(Stack *s, void *obj);
    instead. This will also avoid casting to (void*). Unneccessary castings are
    not good.
    >
    > void *stack_push(Stack *s, const void *obj)
    > {
    > Node *n;
    >
    > assert(s != NULL);
    > assert(obj != NULL);
    > n = malloc(sizeof *n);
    > if (n == NULL)
    > return NULL; /* push failed */
    > n->obj = (void *) obj;
    > n->next = s->top;
    > s->top = n;
    > return (void *) obj; /* successful push */
    > }
    >
    > I can't make the obj field of struct Node a const void *, since the user
    > will probably free it by the "destructor" (I can't free a const void *,
    > right?):


    For,
    const void *obj;
    the statement,
    free ( n -> obj );
    would generate the following message:

    "warning: passing arg 1 of `free' discards qualifiers from pointer target type"

    >
    > void stack_destroy(Stack *s, void (*destroy)(void *))
    > {
    > Node *n, *tmp;
    >
    > assert(s != NULL);
    > n = s->top;
    > while (n != NULL) {
    > tmp = n->next;
    > if (destroy != NULL)
    > destroy(n->obj); /* probably destroy == free */
    > free(n);
    > n = tmp;
    > }
    > free(s);
    > }
    >
     
    Vijay Kumar R Zanvar, Jun 1, 2004
    #2
    1. Advertising

  3. On Mon, 31 May 2004 22:42:29 +0200, Enrico `Trippo' Porreca
    <> wrote:

    >Given:
    >
    > typedef struct Node Node;
    > struct Node {
    > void *obj;
    > Node *next;
    > };
    >
    > typedef struct Stack Stack;
    > struct Stack {
    > Node *top;
    > };
    >
    >...is the following a conforming C function (I am particularly worried
    >about the casts), since I'm not modifying the obj parameter?
    >
    > void *stack_push(Stack *s, const void *obj)


    This says obj is a pointer to a const void. Since there is no way to
    modify a void, the only purpose served by the const is to assure the
    callers of the function that you really won't alter the data obj
    points to.

    > {
    > Node *n;
    >
    > assert(s != NULL);
    > assert(obj != NULL);
    > n = malloc(sizeof *n);
    > if (n == NULL)
    > return NULL; /* push failed */
    > n->obj = (void *) obj;
    > n->next = s->top;
    > s->top = n;
    > return (void *) obj; /* successful push */
    > }
    >
    >I can't make the obj field of struct Node a const void *, since the user
    >will probably free it by the "destructor" (I can't free a const void *,
    >right?):


    What makes you think not? Free doesn't alter to contents of the
    "object" pointed to. It deletes it from existence which is completely
    different. You may have to cast the const void* to a simple void* to
    avoid the diagnostic about incompatible const attributes.

    The real question remains why would you want to? Is it your intent
    that no function in your program ever modify the contents of whatever
    n->obj points to once it has been added to the list?

    >
    > void stack_destroy(Stack *s, void (*destroy)(void *))
    > {
    > Node *n, *tmp;
    >
    > assert(s != NULL);
    > n = s->top;
    > while (n != NULL) {
    > tmp = n->next;
    > if (destroy != NULL)
    > destroy(n->obj); /* probably destroy == free */
    > free(n);
    > n = tmp;
    > }
    > free(s);


    It would be a little odd for s to point to an allocated Stack since
    the struct is so small and there is only one of them. Of course, it's
    also a little odd to have struct with only one member.

    > }




    <<Remove the del for email>>
     
    Barry Schwarz, Jun 1, 2004
    #3
  4. "Barry Schwarz" <> wrote in message
    news:c9h4ro$ss5$1@216.39.135.223...
    > On Mon, 31 May 2004 22:42:29 +0200, Enrico `Trippo' Porreca
    > <> wrote:
    >
    > >Given:
    > >
    > > typedef struct Node Node;
    > > struct Node {
    > > void *obj;
    > > Node *next;
    > > };
    > >
    > > typedef struct Stack Stack;
    > > struct Stack {
    > > Node *top;
    > > };

    ....
    > It would be a little odd for s to point to an allocated Stack since
    > the struct is so small and there is only one of them.


    Why is there only one of them? I see nothing that prevents having more.

    > Of course, it's also a little odd to have struct with only one member.


    Provided the implementation was suitably abstracted, it enables him to add
    other members to struct Stack later without callers needing to know. It
    also keeps types consistent with, say, a doubly-linked list, which needs to
    be a struct to keep track of both ends.

    S

    --
    Stephen Sprunk "Stupid people surround themselves with smart
    CCIE #3723 people. Smart people surround themselves with
    K5SSS smart people who disagree with them." --Aaron Sorkin
     
    Stephen Sprunk, Jun 1, 2004
    #4
  5. Stephen Sprunk wrote:
    > "Barry Schwarz" <> wrote in message
    > news:c9h4ro$ss5$1@216.39.135.223...
    >>On Mon, 31 May 2004 22:42:29 +0200, Enrico `Trippo' Porreca
    >><> wrote:
    >>
    >>>Given:
    >>>
    >>> typedef struct Node Node;
    >>> struct Node {
    >>> void *obj;
    >>> Node *next;
    >>> };
    >>>
    >>> typedef struct Stack Stack;
    >>> struct Stack {
    >>> Node *top;
    >>> };

    >
    > ...
    >
    >>It would be a little odd for s to point to an allocated Stack since
    >>the struct is so small and there is only one of them.


    I'm not allowing the user to have an automatic (or static) struct Stack,
    since the struct definition is hidden in the implementation file. The
    "stack.h" header contains just the typedef. And the user can always
    create an arbitrary number of Stacks.

    >>Of course, it's also a little odd to have struct with only one member.

    >
    > Provided the implementation was suitably abstracted, it enables him to add
    > other members to struct Stack later without callers needing to know. It
    > also keeps types consistent with, say, a doubly-linked list, which needs to
    > be a struct to keep track of both ends.


    In fact I'm also writing a queue (implemented with a struct containing
    two pointers) and some more complicated data structures. Consistency
    (and expandibility) was exactly what I had in mind while writing my code.
     
    Enrico `Trippo' Porreca, Jun 1, 2004
    #5
  6. Barry Schwarz wrote:

    > On Mon, 31 May 2004 22:42:29 +0200, Enrico `Trippo' Porreca
    > <> wrote:
    >
    >>Given:
    >>
    >> typedef struct Node Node;
    >> struct Node {
    >> void *obj;
    >> Node *next;
    >> };
    >>
    >> typedef struct Stack Stack;
    >> struct Stack {
    >> Node *top;
    >> };
    >>
    >>...is the following a conforming C function (I am particularly worried
    >>about the casts), since I'm not modifying the obj parameter?
    >>
    >> void *stack_push(Stack *s, const void *obj)

    >
    > This says obj is a pointer to a const void. Since there is no way to
    > modify a void, the only purpose served by the const is to assure the
    > callers of the function that you really won't alter the data obj
    > points to.


    I wanted to make obj a const void * maily for documentation purposes and
    for consistency with the Standard C Library functions.

    >> {
    >> Node *n;
    >>
    >> assert(s != NULL);
    >> assert(obj != NULL);
    >> n = malloc(sizeof *n);
    >> if (n == NULL)
    >> return NULL; /* push failed */
    >> n->obj = (void *) obj;
    >> n->next = s->top;
    >> s->top = n;
    >> return (void *) obj; /* successful push */
    >> }
    >>
    >>I can't make the obj field of struct Node a const void *, since the user
    >>will probably free it by the "destructor" (I can't free a const void *,
    >>right?):

    >
    > What makes you think not? Free doesn't alter to contents of the
    > "object" pointed to. It deletes it from existence which is completely
    > different.


    Ok, I thought deleting the "object" was not allowed by the const modifier.

    > The real question remains why would you want to? Is it your intent
    > that no function in your program ever modify the contents of whatever
    > n->obj points to once it has been added to the list?


    I want the user to be able to modify the contents of n->obj outside my
    stack management functions if he wants, but I also want to document the
    fact that no function in my library will ever do.

    Should I simply stick to void *?
     
    Enrico `Trippo' Porreca, Jun 1, 2004
    #6
  7. On Tue, 1 Jun 2004, Enrico `Trippo' Porreca wrote:
    >
    > Barry Schwarz wrote:
    > > On Mon, 31 May 2004 22:42:29 +0200, Enrico `Trippo' Porreca wrote:
    > >>
    > >> typedef struct Node Node;
    > >> struct Node {
    > >> void *obj;
    > >> Node *next;
    > >> };
    > >>
    > >> typedef struct Stack Stack;
    > >> struct Stack {
    > >> Node *top;
    > >> };
    > >>
    > >>...is the following a conforming C function (I am particularly worried
    > >>about the casts), since I'm not modifying the obj parameter?
    > >>
    > >> void *stack_push(Stack *s, const void *obj)

    > >
    > > This says obj is a pointer to a const void. Since there is no way to
    > > modify a void, the only purpose served by the const is to assure the
    > > callers of the function that you really won't alter the data obj
    > > points to.

    >
    > I wanted to make obj a const void * mainly for documentation purposes
    > and for consistency with the Standard C Library functions.


    But the "documentation" is incorrect: you actually *do* want to be
    able to modify the target of 'obj' (via 'free', for example). And
    the standard C library is irrelevant, as far as I can tell (except
    insofar as 'free' is part of it, which just supports the no-'const'
    case).

    > >> n->obj = (void *) obj;


    > >> return (void *) obj; /* successful push */


    If you silently cast away the constness of a parameter declared as
    'const', you are doing your client a greater disservice than simply
    declaring it non-const in the first place. Any casts in your code
    should be viewed with *EXTREME* caution and distrust.

    > >>I can't make the obj field of struct Node a const void *, since the user
    > >>will probably free it by the "destructor" (I can't free a const void *,
    > >>right?):

    > >
    > > What makes you think not? Free doesn't alter to contents of the
    > > "object" pointed to. It deletes it from existence which is completely
    > > different.


    Wrong. 'free' deletes the object, and at that point it's allowed to
    do *anything* with the freed block, including writing over its contents.
    So a 'free' written in standard C obviously needs to take a non-'const'
    parameter.
    More importantly, there's no reason to free a 'const' object in C,
    since the only thing you're allowed to free is the result of a 'malloc',
    and you can't put the result of a function call into a 'const' without
    realizing it (and thus realizing that you need to remove the 'const').
    Finally, you cannot legally 'free' a const pointer because the Standard
    says so. 'free' is defined to take a non-const pointer to void, and if
    you pass it a 'const' pointer to void, you're breaking the rules.
    Undefined behavior may well ensue.

    > Ok, I thought deleting the "object" was not allowed by the const modifier.


    You're right. Thus, the solution is to remove the useless 'const'.

    > > The real question remains why would you want to? Is it your intent
    > > that no function in your program ever modify the contents of whatever
    > > n->obj points to once it has been added to the list?

    >
    > I want the user to be able to modify the contents of n->obj outside my
    > stack management functions if he wants, but I also want to document the
    > fact that no function in my library will ever do.


    So document it. The simplest way would be to write, "No function
    in my library will ever change the target of the pointer 'obj'." You
    can get fancier if you want.

    > Should I simply stick to void *?


    Of course. To claim that a pointer whose contents *can* and *will*
    be modified (no matter by whom) is 'const' is simply poor documentation.

    -Arthur
     
    Arthur J. O'Dwyer, Jun 1, 2004
    #7
  8. Arthur J. O'Dwyer wrote:
    > On Tue, 1 Jun 2004, Enrico `Trippo' Porreca wrote:
    >
    >>I want the user to be able to modify the contents of n->obj outside my
    >>stack management functions if he wants, but I also want to document the
    >>fact that no function in my library will ever do.

    >
    > So document it. The simplest way would be to write, "No function
    > in my library will ever change the target of the pointer 'obj'." You
    > can get fancier if you want.
    >
    >>Should I simply stick to void *?

    >
    > Of course. To claim that a pointer whose contents *can* and *will*
    > be modified (no matter by whom) is 'const' is simply poor documentation.


    So I guess it'll be void * (as in my first version :)).
     
    Enrico `Trippo' Porreca, Jun 1, 2004
    #8
  9. In article <>, "Arthur J. O'Dwyer" <> writes:
    > On Tue, 1 Jun 2004, Enrico `Trippo' Porreca wrote:
    > >
    > > I wanted to make obj a const void * mainly for documentation purposes
    > > and for consistency with the Standard C Library functions.

    >
    > But the "documentation" is incorrect: you actually *do* want to be
    > able to modify the target of 'obj' (via 'free', for example).


    I'll agree with Arthur here. stack_push itself does not modify obj,
    which suggests that obj ought to be declared as const void * (so that
    const data could be added to the stack). However, the suggestion is
    misleading, because stack_push puts obj in the line of free's fire,
    so to speak. In effect it schedules (or may schedule) obj for later
    freeing, which is an operation with side effects on obj.

    So while "parameter P may be const if function does not have side
    effects that affect P" is normally a good rule of thumb, it's not the
    whole story for const-correctness. If the function exposes P to other
    functions, that expands the scope in which P's constness must be
    considered. Fortunately, in this case C's type system (and a
    cooperating implementation) was sufficient to notify you of that: try
    to make P const, and at some point you'll need a cast (because of
    that eventual call to free).

    --
    Michael Wojcik

    It's like being shot at in an airport with all those guys running
    around throwing hand grenades. Certain people function better with
    hand grenades coming from all sides than other people do when the
    hand grenades are only coming from inside out. -- Dick Selcer
     
    Michael Wojcik, Jun 2, 2004
    #9
  10. Enrico `Trippo' Porreca

    Ralmin Guest

    "Arthur J. O'Dwyer" <> wrote:
    [...]
    > More importantly, there's no reason to free a 'const'
    > object in C, since the only thing you're allowed to free
    > is the result of a 'malloc', and you can't put the result
    > of a function call into a 'const' without realizing it
    > (and thus realizing that you need to remove the 'const').


    What do you mean by "realizing it"?

    #include <stdlib.h>
    int main(void)
    {
    const int *p = malloc(10 * sizeof *p);
    return 0;
    }

    Is this not correct C code?

    --
    Simon.
     
    Ralmin, Jun 2, 2004
    #10
  11. On Wed, 2 Jun 2004, Ralmin wrote:
    >
    > "Arthur J. O'Dwyer" <> wrote:
    > [...]
    > > More importantly, there's no reason to free a 'const'
    > > object in C, since the only thing you're allowed to free
    > > is the result of a 'malloc', and you can't put the result
    > > of a function call into a 'const' without realizing it
    > > (and thus realizing that you need to remove the 'const').

    >
    > What do you mean by "realizing it"?


    Seeing it. Making it conspicuous to yourself. Observing
    it clearly and consciously.

    > #include <stdlib.h>
    > int main(void)
    > {
    > const int *p = malloc(10 * sizeof *p);
    > return 0;
    > }
    >
    > Is this not correct C code?


    Legal, yes. Correct, no; you forgot to 'free' the result of
    'malloc' before the end of the program. My point is, you can't
    generally write something like the above without at some point
    seeing 'const' and 'malloc' on the same line, and thus realizing
    "whoops, I'm probably doing something dumb here." [Pathological
    cases abound, yes, but in practice I don't think you're likely
    to malloc into a const object without noticing it.]

    The same kind of argument applies to casts: you can't do a
    lot of dubious things in C without using a cast, and that cast
    should make you *realize* that you're doing something dangerous
    and/or silly.

    HTH,
    -Arthur
     
    Arthur J. O'Dwyer, Jun 2, 2004
    #11
  12. In article <>,
    Arthur J. O'Dwyer <> wrote:

    > Legal, yes. Correct, no; you forgot to 'free' the result of
    >'malloc' before the end of the program.


    There is often no point in freeing malloced memory. It's certainly
    not "incorrect" in general to leave memory unfreed.

    -- Richard
     
    Richard Tobin, Jun 2, 2004
    #12
  13. On Wed, 2 Jun 2004, Richard Tobin wrote:
    >
    > Arthur J. O'Dwyer <> wrote:
    > > Legal, yes. Correct, no; you forgot to 'free' the result of
    > >'malloc' before the end of the program.

    >
    > There is often no point in freeing malloced memory. It's certainly
    > not "incorrect" in general to leave memory unfreed.


    It's a question of definitions, then. If you're willing to accept
    the memory leak, or you are willing to assume there won't be a
    memory leak, then that's all right. But in general, if you allocate
    a block of memory from the implementation and then don't give it
    back, that's IMO "incorrect." You ought to free everything you
    malloc.
    If your system, like many *nixes, frees all allocated memory as
    part of program termination, then by all means go ahead and don't
    free anything yourself. But don't expect anyone else to use your
    non-portable code. You see the point now?

    -Arthur
     
    Arthur J. O'Dwyer, Jun 3, 2004
    #13
  14. In article <>,
    Arthur J. O'Dwyer <> wrote:

    >But in general, if you allocate
    >a block of memory from the implementation and then don't give it
    >back, that's IMO "incorrect."


    It might be, IYO, "incorrect", but it's not incorrect.

    >You ought to free everything you malloc.


    This would be the Morality appendix to the C standard?

    >If your system, like many *nixes, frees all allocated memory as
    >part of program termination, then by all means go ahead and don't
    >free anything yourself.


    As you must have been aware, I did not suggest never freeing anything.
    Be more subtle in your hyperbole.

    Portability is not an absolute. It's a matter of practicalities.
    Many of my programs won't run on a machine with 64K of memory. Many
    of them won't run on non-Posix systems. Do I care? Similarly, if you
    want programs that run on PDAs or embedded devices that don't have an
    operating system that reclaims memory, feel free to write or port
    them, but that's not usually my priority. If I judge that it's
    necessary to avoid memory leakage, I take care to avoid it, and if it
    isn't I don't worry.

    >You see the point now?


    No. I see a rule-of-thumb turned into a fetish.

    -- Richard
     
    Richard Tobin, Jun 4, 2004
    #14
  15. On Fri, 04 Jun 2004 00:44:19 +0000, Richard Tobin wrote:

    > In article <>,
    > Arthur J. O'Dwyer <> wrote:
    >
    >>But in general, if you allocate
    >>a block of memory from the implementation and then don't give it
    >>back, that's IMO "incorrect."

    >
    > It might be, IYO, "incorrect", but it's not incorrect.


    Not just his opinion. Plenty of others share his view, because plenty of
    people have been bitten by memory leaks created by lazy programmers like
    you.

    >
    >>You ought to free everything you malloc.

    >
    > This would be the Morality appendix to the C standard?


    No, it would be common sense. Consider engaging your brain.

    >
    >>If your system, like many *nixes, frees all allocated memory as
    >>part of program termination, then by all means go ahead and don't
    >>free anything yourself.

    >
    > As you must have been aware, I did not suggest never freeing anything.
    > Be more subtle in your hyperbole.


    But his subtlety would be lost on you, and then you'd whine.

    >
    > Portability is not an absolute. It's a matter of practicalities.


    Bullshit. You can write C code that's portable to all systems which have
    conforming compilers.

    > If I judge that it's
    > necessary to avoid memory leakage, I take care to avoid it,


    And you can always know this how... ?

    >
    >>You see the point now?

    >
    > No. I see a rule-of-thumb turned into a fetish.


    Fools see a very different world indeed.


    --
    yvoregnevna gjragl-guerr gjb-gubhfnaq guerr ng lnubb qbg pbz
    To email me, rot13 and convert spelled-out numbers to numeric form.
    "Makes hackers smile" makes hackers smile.
     
    August Derleth, Jun 4, 2004
    #15
    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. David M. Wilson
    Replies:
    8
    Views:
    492
    Ben Pfaff
    Jan 7, 2004
  2. Twister

    Casting void * to void ** ?

    Twister, Apr 21, 2006, in forum: C Programming
    Replies:
    31
    Views:
    925
    Joe Smith
    May 4, 2006
  3. Replies:
    11
    Views:
    1,114
  4. Javier
    Replies:
    2
    Views:
    574
    James Kanze
    Sep 4, 2007
  5. 0m
    Replies:
    26
    Views:
    1,126
    Tim Rentsch
    Nov 10, 2008
Loading...

Share This Page