2

I have a question about best practices involving pointers in function parameters and whether they should be specified as *const or const *const. I know there are varying opinions on use, or excessive use, of const, but at least some use is a good way to catch accidental mistakes.

Say you have the following function, which is part of the implementation of a queue based on a linked list:

void enqueue(struct queue *q, void *data)
{
    struct queue_node *item = malloc(sizeof(*item));
    item->data = data;

    [add node to linked list, specifics are not relevant here]
}

struct queue_node is defined as

struct queue_node {
        struct queue_node *next;
        void *data;
};

data is not meant to be modified in this function, so it seems like a good idea for data to be of type void *const so you can't accidentally do something like data = item->data instead of item->data = data.

The next level would be to define data as const void *const which means you also can't change what data points to. However, this leads to a warning about discarding the const qualifier from the pointer. And if I understand correctly, further use of such a pointer is considered undefined behavior. Using a cast (item->data = (void *)data) seems clunky.

What's the best approach here? Is const void *const unnecessarily over-protective? It feels like void *const is sufficient to catch most errors.

1
  • 1
    If item->data has type void *, I think it makes sense the the data parameter to be void * or void * const even though the function does not modify the object pointed to by data. I would only use const void * data or const void * const data if item->data had type const void *. Commented May 12, 2021 at 15:19

1 Answer 1

2

Since changes to function parameters aren't reflected in the calling function, there's little reason to make sure the parameters themselves are read-only. It is useful however to ensure that what a pointer parameter points to doesn't change. So if you don't want what data points to to be changed, you would change the function signature to:

void enqueue(struct queue *q, const void *data)

Note that this will work only if the data member of struct queue_node has type const void *. If it doesn't, then you don't want to use const on the data parameter.

Sign up to request clarification or add additional context in comments.

3 Comments

(original post edited to include struct definition) data is type void * so later retrieval can be cast-less and the object pointed to can be modified.
@sj95126 That means the function doesn't guarantee data won't be modified and therefore the parameter should not be const.
OK, thanks. It sounds like the solution is to just stick with void *. This won't protect against something like data = item->data instead of item->data = data but the effect of that should be obvious when there's an item in the queue with missing or corrupt data.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.