Casting const void * into void *

  • Thread starter Enrico `Trippo' Porreca
  • Start date
E

Enrico `Trippo' Porreca

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);
}
 
V

Vijay Kumar R Zanvar

Enrico `Trippo' Porreca said:
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"
 
B

Barry Schwarz

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>>
 
S

Stephen Sprunk

Barry Schwarz said:
....
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
 
E

Enrico `Trippo' Porreca

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.
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.
 
E

Enrico `Trippo' Porreca

Barry said:
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.
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 *?
 
A

Arthur J. O'Dwyer

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).

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.

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'.
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
 
E

Enrico `Trippo' Porreca

Arthur said:
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.


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 :)).
 
M

Michael Wojcik

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 (e-mail address removed)

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
 
R

Ralmin

Arthur J. O'Dwyer said:
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?
 
A

Arthur J. O'Dwyer

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
 
R

Richard Tobin

Arthur J. O'Dwyer said:
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
 
A

Arthur J. O'Dwyer

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
 
R

Richard Tobin

Arthur J. O'Dwyer said:
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
 
A

August Derleth

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.
This would be the Morality appendix to the C standard?

No, it would be common sense. Consider engaging your brain.
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... ?
No. I see a rule-of-thumb turned into a fetish.

Fools see a very different world indeed.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top