H
hzmonte
Correct me if I am wrong, declaring formal parameters of functions as
const, if they should not be/is not changed, has 2 benefits;
1. It tells the program that calls this function that the parameter
will not be changed - so don't worry.
2. It tells the implementor and the maintainer of this function that
the parameter should not be changed inside the function. And it is for
this reason that some people advocate that it is a good idea to
const-ify even a non-pointer parameter:
void my_func(const int i) { ... }
because if the original programmer or a subsequent maintainer
accidentally change the value of the parameter, the compiler can catch
it.
A third benefit may be that the compiler can optimize the program if it
knows for sure that the parameter is not changed inside the function. I
am not sure about this though.
Now, I would like to benefit from the const-ification of the function
parameter. Let's say I have this code:
typedef struct s_fifoItem t_fifoItem;
struct s_fifoItem {
t_fifoItem *next;
t_fifoItem *prev;
void *elem;
};
typedef t_fifoItem t_fifoQueue;
void enqueue(t_fifoQueue *q, void *elem)
{
t_fifoItem *i, *last;
i = malloc(sizeof(t_fifoItem));
i->elem = elem;
last = q->prev;
last->next = i;
i->next = q;
q->prev = i;
}
elem appears to be a good candidate for const-ification. Intutitively
the program calling enqueue() normally does not expect that the item to
be enqueued is to be changed. And the implementor of enqueue() would
not want to change elem either; and actually elem is not changed in the
above implementation. But if I const-ify elem, gcc would complain (in a
warning) that the assignment
i->elem = elem;
would discard the const qualifier because the type of "i->elem" is
"void *", not "const void *". One solution is to cast away the
const-ness of elem in that assignment:
i->elem = (void *)elem;
Another solution is to change the declaration of elem in struct
s_fifoItem to be:
const void *elem;
The latter solution is not practical because there are other places
that will change *elem.
However, the former solution of casting away the const-ness is not very
appealing. It is kind of cheating.
So, how do people resolve this? And to a deeper level, gcc seems to be
caught bewteen 2 conflicting goals: ensuring type correctness (a double
cannot be assigned to an int, a const void * cannot be assigned to a
void *, etc) and ensuring the unmodifiability of a variable. From the
point of view of language design, what are the issues involved and how
do other programming languages deal with it?
P.S. I know nothing about C++. But in its queue STL, there is a
function
void push(const T &val)
The const here means that the element is a const parameter. This
confirms my belief that it is good programming practice to const-ify
the element to be enqueued. But I wonder how this const-ification is
implemented inside push(). It is possible that the parameter is made
constant in the prototype from the client's perspective (to reap the
first benefit I mentioned above) but the parameter is cast (void *)
inside the function (so the second benefit is not reaped, and the STL
coders (i.e. ones who code the STL) need to be careful).
const, if they should not be/is not changed, has 2 benefits;
1. It tells the program that calls this function that the parameter
will not be changed - so don't worry.
2. It tells the implementor and the maintainer of this function that
the parameter should not be changed inside the function. And it is for
this reason that some people advocate that it is a good idea to
const-ify even a non-pointer parameter:
void my_func(const int i) { ... }
because if the original programmer or a subsequent maintainer
accidentally change the value of the parameter, the compiler can catch
it.
A third benefit may be that the compiler can optimize the program if it
knows for sure that the parameter is not changed inside the function. I
am not sure about this though.
Now, I would like to benefit from the const-ification of the function
parameter. Let's say I have this code:
typedef struct s_fifoItem t_fifoItem;
struct s_fifoItem {
t_fifoItem *next;
t_fifoItem *prev;
void *elem;
};
typedef t_fifoItem t_fifoQueue;
void enqueue(t_fifoQueue *q, void *elem)
{
t_fifoItem *i, *last;
i = malloc(sizeof(t_fifoItem));
i->elem = elem;
last = q->prev;
last->next = i;
i->next = q;
q->prev = i;
}
elem appears to be a good candidate for const-ification. Intutitively
the program calling enqueue() normally does not expect that the item to
be enqueued is to be changed. And the implementor of enqueue() would
not want to change elem either; and actually elem is not changed in the
above implementation. But if I const-ify elem, gcc would complain (in a
warning) that the assignment
i->elem = elem;
would discard the const qualifier because the type of "i->elem" is
"void *", not "const void *". One solution is to cast away the
const-ness of elem in that assignment:
i->elem = (void *)elem;
Another solution is to change the declaration of elem in struct
s_fifoItem to be:
const void *elem;
The latter solution is not practical because there are other places
that will change *elem.
However, the former solution of casting away the const-ness is not very
appealing. It is kind of cheating.
So, how do people resolve this? And to a deeper level, gcc seems to be
caught bewteen 2 conflicting goals: ensuring type correctness (a double
cannot be assigned to an int, a const void * cannot be assigned to a
void *, etc) and ensuring the unmodifiability of a variable. From the
point of view of language design, what are the issues involved and how
do other programming languages deal with it?
P.S. I know nothing about C++. But in its queue STL, there is a
function
void push(const T &val)
The const here means that the element is a const parameter. This
confirms my belief that it is good programming practice to const-ify
the element to be enqueued. But I wonder how this const-ification is
implemented inside push(). It is possible that the parameter is made
constant in the prototype from the client's perspective (to reap the
first benefit I mentioned above) but the parameter is cast (void *)
inside the function (so the second benefit is not reaped, and the STL
coders (i.e. ones who code the STL) need to be careful).