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 restrict
ed and the unexpected result of printing 1 inside the function is a bug caused by the caller code.