Let\'s say we have a class called object.
int main(){
object a;
const object* b = &a;
(*b);
}
Question: b is a pointer to
By writing
const object* b = &a;
you declare that b
is a pointer (*
) to a const
of type object
, to which you then assign the address of a
. a
is of type object
(but not const); you are permitted to use the adress of the non-const object
in place of an adress of an const object
.
When you dereference *
b however, the compiler can only go according to your declaration - thus *b
is a const object
(however you can still modify a
as you like, so beware of thinking that the object b
points to cannot change - it mere cannot be changed via b
)
Here’s a specific example of why it is the way it is. Let’s say you declare:
int a[] = {1, 2, 3};
constexpr size_t n_a = sizeof(a)/sizeof(a[0]);
extern int sum( const int* sequence, size_t n );
sum_a = sum( a, n_a );
Now you implement sum()
in another module. It doesn’t have any idea how the original object you’re pointing to was declared. It would be possible to write a compiler that tagged pointers with that information, but no compilers in actual use today do, for a number of good reasons.¹
Inside sum()
, which might be in a shared library that cannot be recompiled with whole-program optimization, all you see is a pointer to memory that cannot be altered. In fact, on some implementations, trying to write through a pointer to const
might crash the program with a memory-protection error. And the ability to reinterpret a block of memory as some other type is important to C/C++. For example, memset()
or memcpy()
reinterprets it as an array of arbitrary bytes. So the implementation of sum()
has no way to tell the provenance of its pointer argument. As far as it’s concerned, it’s just a pointer to const int[]
.
More importantly, the contract of the function says that it’s not going to modify the object through that pointer.² It could simply cast away the const
qualifier explicitly, but that would be a logic error. If you’re declaring a pointer const
, it’s like engaging the safety on your gun: you want the compiler to stop you from shooting yourself in the foot.
¹ Including extra instructions to extract the address from a pointer, extra memory to store them, compatibility with the standard calling convention for the architecture, breaking a lot of existing code that assumes things like long
being able to hold a pointer, and the lack of any benefit.
² With the pedantic exception of mutable
data members.
const object* b = &a;
b will treat what it points to as const
, i.e. it cannot change a
object* const b = &a;
b itself is const
, i.e. it cannot point to other object
address, but it can change a
Because the const
keyword means 'you can not modify that object' rather than 'the object can not be modified'.
This is useful when you pass an object to some function to use the object's value only, but but not to modify it:
// We expect PrintMyString to read the string
// and use its contents but not to modify it
void PrintMyString(const char *string);
void myfunction(int number)
{
// build and print a string of stars with given length
if(number>0 && number<=16]
{
char string[17];
int i;
for(i=0, i<number; i++)
string[i] = '*';
string[number] = '\0';
PrintMyString(string);
}
}
The function PrintMyString
will get an array of characters, but the array will be passed as 'read only' to the function. The array is certainly modifiable within the owning function, but the PrintMyString
can only read what it gets and not alter the contents.