Home > Software design >  Should I set argument to pointer to const if it might be changed?
Should I set argument to pointer to const if it might be changed?

Time:04-22

For example, given a function

void func(const int* a, int* b){
    *b = 2   *a;
}

Should I emmit the const if func is often called like so: func(ptr, ptr) ?

CodePudding user response:

The word const in your function definition documents that the function will not change the object whose address is passed as the first argument to the function. It says that a is a pointer to a "constant integer".

So if your function does not change the object that a points to, it makes sense to keep the const.

While it is not necessary to include the const, it is good practice to do so. If you do not intend to change the object that a points to but your code attempts to modify *a anyways, this should be an error that your compiler will detect.

CodePudding user response:

Short answer:

If the function is supposed to be called with the same pointer in both parameters, you should not use const. Otherwise, if the function does not support both parameters being the same and what a points at shouldn't be modified, the const should be there as part of "const correctness".


Long answer:

const does not guarantee that a pointer is an unique alias. Rather, de-referencing a const int* is allowed to be an alias to de-referencing an int* as one of the exceptions to the so-called "strict aliasing rule", see the list of exceptions below C17 6.5:

An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
...

  • a qualified version of a type compatible with the effective type of the object

In practice, check out this example:

#include <stdio.h>

void func(const int* a, int* b)
{
    int tmp = *a;
    printf("%d\n", tmp);

    *b = 2   *a;

    tmp = *a;
    printf("%d\n", tmp);
}

int main (void)
{
    int x = 1;
    int* ptr=&x;
    func(ptr,ptr);
    printf("%d\n",*ptr);
}

Compiled with gcc 11.2 or clang 14.0.0 x86_64 -O3, this prints:

1
3
3

That is, the value in tmp was reloaded since the compiler couldn't assume that the lvalue *a was not modified by the previous code.

If we change this code to:

void func(const int* restrict a, int* b)

Then the output turns into:

1
1
3

restrict is a contract between the compiler and the programmer that all access of what a points at goes through the pointer a. Since the compiler is now free to assume that a and b do not alias, it can optimize out the tmp = *a; line, since tmp already contains the value of *a.

In this specific example the programmer lies to the compiler by calling the function as func(ptr,ptr) despite arguments being restricted and the unexpected result of printing 1 inside the function is a bug caused by the caller code.

  • Related