Why can't I modify a variable through a const pointer? [duplicate] - c++

This question already has answers here:
Const before or const after?
(9 answers)
Closed 7 years ago.
Look at this code:
int main() {
int foo = 0;
const int *ptr = &foo;
*ptr = 1; // <-- Error here
return 0;
}
When compiling, clang gives me an error:
const.c:5:7: error: read-only variable is not assignable
Yes, ptr is const, but foo isn't. Why can't I assign a value to foo?

You need to differentiate these:
const int *ptr = &foo; // <-- NON-CONST pointer, CONST data.
// ptr _can_ point somewhere else, but what it points to _cannot_ be modified.
int * const ptr = &foo; // <-- CONST pointer, NON-CONST data.
// ptr _cannot_ point to anywhere else, but what it points to _can_ be modified.
const int * const ptr = &foo; // <-- CONST pointer, CONST data.
// ptr _cannot_ point to anywhere else, and what it points to _cannot_ be modified.

const int * is a pointer to an integer constant.
That means, the integer value that it is pointing at cannot be changed using that pointer. Even though the memory where the value is stored, (foo) was declared a normal variable and not const, but the variable you are using to change the value if of type pointer to an integer constant.
Now, you can change the value using foo, but not that pointer.
There is a difference between pointer to an integer constant and constant pointer to an integer.
constant pointer to an integer is defined as int * const. You cannot make the pointer point to some other memory once you have initialized.
pointer to an integer constant is defined as int const * or const int *. You cannot change the value pointed to by the pointer through this pointer itself.
NOTE:
Use the standard definition of main()
int main(void) //if no command line arguments.

Related

Why can't I assign value of pointer to const int to a pointer of int? [duplicate]

This question already has answers here:
const pointer assign to a pointer
(1 answer)
C++ - can't assign top-const pointer to another non-const pointer
(2 answers)
Assigning const int to a const pointer to int is illegal?
(2 answers)
Closed 2 years ago.
[Note: I saw another question asking about the same section in the same book, but for different reasons]Reading through the C++ primer book and was going through the section "top-level consts". Here it writes that:
The distinction between top-level and low-level matters when we copy an object.
When we copy an object, top-level consts are ignored
When we copy an object,
both objects must have the same low-level const qualification...
This I understood but I went ahead and manipulated an example given in the book multiple times to further my understanding with different scenarios. The code below is one such scenario:
int i = 5;
int *p = &i;
const int *const ptr = p;
p = ptr;
I had hypothesised before I ran the program that, Line 4 would be legal. However, intellisense instead threw me this error:
A value of type "const int *" cannot be assigned to an entity of type "int *"
Now I've done other things like *p = *ptr (which was legal), ptr = p (which would obviously be illegal) and changed the object i to const (which would henceforth require me to change p and satisfy the condition that copying an object must have the same low-level const). And in all cases, I've understood why.
But not for this scenario. Can someone explain why this is illegal?
First consider this example:
void foo(const int* p) {
*x = 1; // Error ! x is pointer to const
}
int x = 42;
foo(&x);
x is not const. Nevertheless, inside foo you are not allowed to modify its value via the const int* p, because it is a pointer to a const int. Having a pointer to const int does not imply that the int pointed to is actually const. It only means: You are not allowed to modify the int via that pointer.
The compiler does not consider what pointer you actually pass to see that *x = 1; is not allowed inside foo.
An int * on the other hand, does allow to modify the pointed to int. Now consider this:
const int x = 42;
const int* p = &x;
int* p2 = p; // Error ! why?
*p2 = 0; // <- because of this
If you were allowed to assign the const int* to a int* this would create a hole in const correctness, because a int* does allow you to modify the pointee.
The top level const in
const int* const p3 = &x;
refers to the pointer itself. You cannot modify the pointer. While you can modify this one:
const int* p4;
p4 = &x;

Const reference vs pointer to const

Why using pointer to const I can change value of variable the pointer points to while using const reference I cannot?
int a = 10;
int * const ptr = &a;
(*ptr)++; // OK
const int & ref = a;
ref++; // error
In the first example, you have a const pointer to a non-const int. This is, because const applies to the thing left of it, except there is nothing at the left side, then it applies to the right thing. Therefore, it's OK to increment the value, but it would be forbidden to increment the pointer (increment without dereferencing). In the second example, you try to increment a const reference, which is not allowed.
"const int *ptr" means that the data pointed to by the pointer is constant and cannot be changed while the pointer itself can change.
"int * const ptr" means that the pointer cannot point somewhere else while the data pointed to can change.
So your first increment works while the second one gives an error.
for fully understanding read the following ::
const int value = 5; // value is const
int *ptr = &value; // compile error: cannot convert const int* to int*
*ptr = 6; // change value to 6
The above snippet won’t compile -- we can’t set a non-const pointer to a const variable. This makes sense: a const variable is one whose value can not be changed. Hypothetically, if we could set a non-const pointer to a const value, then we would be able to dereference the non-const pointer and change the value. That would violate the intention of const.
2-Pointer to const value
A pointer to a const value is a (non-const) pointer that points to a constant value.
To declare a pointer to a const value, use the const keyword before the data type:
const int value = 5;
const int *ptr = &value; // this is okay, ptr is a non-const pointer that is pointing to a "const int"
*ptr = 6; // not allowed, we can't change a const value
In the above example, ptr points to a const int.
So far, so good, right? Now consider the following example:
1
2
int value = 5; // value is not constant
const int *ptr = &value; // this is still okay
A pointer to a constant variable can point to a non-constant variable (such as variable value in the example above). Think of it this way: a pointer to a constant variable treats the variable as constant when it is accessed through the pointer, regardless of whether the variable was initially defined as const or not.
Thus, the following is okay:
int value = 5;
const int *ptr = &value; // ptr points to a "const int"
value = 6; // the value is non-const when accessed through a non-const identifier
But the following is not:
int value = 5;
const int *ptr = &value; // ptr points to a "const int"
*ptr = 6; // ptr treats its value as const, so changing the value through ptr is not legal
Because a pointer to a const value is not const itself (it just points to a const value), the pointer can be redirected to point at other values:
int value1 = 5;
const int *ptr = &value1; // ptr points to a const int
int value2 = 6;
ptr = &value2; // okay, ptr now points at some other const int
3-Const pointers
We can also make a pointer itself constant. A const pointer is a pointer whose value can not be changed after initialization
To declare a const pointer, use the const keyword between the asterisk and the pointer name:
int value = 5;
int *const ptr = &value;
Just like a normal const variable, a const pointer must be initialized to a value upon declaration. This means a const pointer will always point to the same address. In the above case, ptr will always point to the address of value (until ptr goes out of scope and is destroyed).
int value1 = 5;
int value2 = 6;
int * const ptr = &value1; // okay, the const pointer is initialized to the address of value1
ptr = &value2; // not okay, once initialized, a const pointer can not be changed.
However, because the value being pointed to is still non-const, it is possible to change the value being pointed to via dereferencing the const pointer:
int value = 5;
int *const ptr = &value; // ptr will always point to value
*ptr = 6; // allowed, since ptr points to a non-const int
4-Const pointer to a const value
Finally, it is possible to declare a const pointer to a const value by using the const keyword both before the type and before the variable name:
int value = 5;
const int *const ptr = &value;
A const pointer to a const value can not be set to point to another address, nor can the value it is pointing to be changed through the pointer.
Recapping
To summarize, you only need to remember 4 rules, and they are pretty logical:
A non-const pointer can be redirected to point to other addresses.
A const pointer always points to the same address, and this address can not be changed.
A pointer to a non-const value can change the value it is pointing to. These can not point to a const value.
A pointer to a const value treats the value as const (even if it is not), and thus can not change the value it is pointing to.
Keeping the declaration syntax straight can be challenging. Just remember that the
int value = 5;
const int *ptr1 = &value; // ptr1 points to a "const int", so this is a pointer to a const value.
int *const ptr2 = &value; // ptr2 points to an "int", so this is a const pointer to a non-const value.
const int *const ptr3 = &value; // ptr3 points to a "const int", so this is a const pointer to a const value.
Conclusion
Pointers to const values are primarily used in function parameters (for example, when passing an array to a function) to help ensure the function doesn’t inadvertently change the passed in argument. We will discuss this further in the section on functions.
resource :: learncpp
There is a difference between a const pointer and a pointer to a const variable, depending on where you put the const qualifier:
int main()
{
int a = 10;
int* const ptr1 = &a;
(*ptr1)++; // OK - This is a const pointer to a non-const int
const int* ptr2 = &a;
(*ptr2)++; // Error - but THIS is a pointer to a const int
return 0;
}

confusion on constant pointer [duplicate]

This question already has answers here:
What is the difference between const int*, const int * const, and int const *?
(23 answers)
Closed 5 years ago.
int p=10;
const int * ptr=&p; // expression 1
As far as i understood by expression 1 that the data which is pointed by pointer ptr is constant
so if i write
*ptr=10;
which is invalid ,
but if i take another pointer variable like
int * pr=&p;
*pr=19;
cout<<*ptr;
will give me the ouput 19
so now the data pointed by ptr changed
but earlier we have seen that data pointed by ptr is constant
why data is changed by another pointer variable?
const int * ptr=&p; means the data pointed to by ptr is const, but only relative to that pointer.
The pointed-to data is not necessarily really const (=originally declared const) and if it isn't, non-const pointers to it (including the original const-pointer cast to its non-const version) may change it.
If some data is really const, attempts to modify it through an non-const pointer result in undefined behavior.
This is very basic, so my suggestion is to read a basic C++ book.
Despite of that I'll provide the answer.
int p = 10;
It is a statement which declares and defines a variable named p of type int.
The content of this variable can be modified. That's because the variable p is not const.
Obviously the later statement p = 13; is still valid and it assigns a new value to that variable.
Now you have this:
const int* ptr = &p;
You're defining a pointer, named ptr which points to that variable.
Adding the qualifier const to the pointer it simply means that you cannot modify the content of the variable by means of the access of the pointer itself.
In other words, the pointer can be only used (for example) for reading the value of p.
On the other hand:
int* pr = &p;
defines a pointer which is not more const qualified.
Indeed, you can access and modify the content of the variable p by means of the usage of that pointer itself. (*pr = 19; is a valid statement).
A little bit far...
This is the general idea behind behind a "more complex world".
The statement:
const int* ptr = &p;
it's possible because the a variable can be implicitly converted in its const version.

Changing constant pointer's address

I'm trying to understand one thing.
I know I can't change constant pointer's value, but I can change its address, if I initialize a pointer the following way:
int foo = 3;
const int *ptr = &foo;
*ptr = 6; // throws an error
int bar = 0;
ptr = &bar; // foo == 0
Now, let's say I declare (/define, I never remember which one) a function:
void change(const int arr[], int size);
int main() {
int foo[2] = {};
change(foo, 2);
std::cout << foo[0];
}
void change(const int arr[], int size) {
// arr[0] = 5 - throws an error
int bar = 5;
arr = &bar;
}
The last line in the code above doesn't throw any errors. However, when the function is over and I display the first element, it shows 0 - so nothing has changed.
Why is that so?
In both situations I have constant pointers, and I try to change its address. In the first example it works. In the second one it doesn't.
I also have another question. I've been told that if I want to pass two-pointers type to the function, const keyword won't work as expected. Is that true? And if so, then what's the reason?
You're screwing up the terminology a lot, so I'm going to start there because I think it is a major cause of your confusion. Consider:
int x;
int* p = &x;
x is an int and p is a "pointer to int". To modify the value of p means to change p itself to point somewhere else. A pointers value is the address it holds. This pointer p holds an address of an int object. To change the pointer's value doesn't mean to change the int object. For example, p = 0; would be modifying p's value.
In addition to that, the address of p is not the address it holds. The address of p would be what you get if you did &p and would be of type "pointer to pointer to int". That is, the address of p is where you would find the pointer p in memory. Since an object doesn't move around in memory, there's no such thing as "changing its address".
So now that's out of the way, let's understand what a constant pointer is. const int* is not a constant pointer. It's a pointer to a constant object. The object it points to is constant, not the pointer itself. A constant pointer type would look more like int* const. Here the const applies to the pointer, so it is of type "const pointer to int".
Okay, now I'll quickly give you an easy way to remember the difference between declaration and definition. If you bought a dictionary and all it had was a list of words in it, would you really call it a dictionary? No, a dictionary is supposed to filled with definitions of words. It should tell you what those words mean. The dictionary with no definition is only declaring that such words exist in the given language. So a declaration says that something exists, and a definition gives the meaning of it. In your case:
// Declaration
void change(const int arr[], int size);
// Definition
void change(const int arr[], int size) {
// arr[0] = 5 - throws an error
int bar = 5;
arr = &bar;
}
Now to explain the issue here. There's no such thing as an array argument type. Any array type argument is converted to a pointer. So the declaration of change is actually identical to:
void change(const int arr*, int size);
when you do arr = &bar; you are simply assigning the address of bar to the pointer arr. That has no effect on the array elements that arr is pointing to. Why should it? You are simply changing where arr points to, not the objects it points at. And in fact you can't change the objects it points at because they are const ints.
I know I can't change constant pointer's value, but I can change its address
Nah. You can't change the address of anything. Did you mean that you can't change the object it points to, but you can change the pointer itself? Because that's what is the truth - in the case of a pointer-to-const type. However, if you have a const pointer to a non-const object, then you can't change the pointer, you can only change whatever it points to.
Addendum (edit): a handy rule of thumb is that const applies to what stands on its left side, except when nothing stands on its left side, because then it applies to the type that is on its right side. Examples:
const int *ptr;
int const *ptr; // these two are equivalent: non-const pointer to const int
int *const ptr; // const pointer to non-const int
int const *const ptr; // const pointer to const int
const int *const ptr; // same as above
However, when the function is over and I display the first element, it shows 0 - so nothing has changed.
Scope. arr is a function argument - so it's local to the function. Whatever you do with it, it won't be effective outside of the function. To achieve what you want, declare it as a reference:
void change(const int *&arr, int size)
I've been told that if I want to pass two-pointers type to the function, const keyword won't work as expected. Is that true?
This depends on what your expectations are. If you read the standard attentively and have proper expectations, then it will indeed work as expected. Examples:
const int **ptr; // pointer to pointer to const int
int const **ptr; // same as above
const int *const *ptr; // pointer to const pointer to const int
etc. You can generate more of these funky declarations using CDecl
The first thing is using the proper terms, which actually helps in understanding:
const int *ptr = &foo;
That is a pointer to a constant integer, not a constant pointer to an integer. You cannot change the object pointed, but you can change the pointer to refer to a different object.
void change(const int arr[], int size);
That signature is processed by the compiler as void change( const int *arr, int size ), and I'd recommend that you type it as that, as it will reduce confusions. Where the function is called, change(foo,2), the compiler will transform the argument foo (type is int[2]) to &foo[0] which has type const int* (both transformations are commonly called decay of the array to a pointer).
Now as in the first block of code, you cannot change the pointed memory, but you can change the pointer to refer to a different object.
Additionally, in C++ the default mode is pass-by-value. The pointer arr inside change is a copy of the value &foo[0]. Inside the function you are changing that copy, but that will not affect anything outside of the function context.
const int * is doing what it's supposed to do, what's confusing you is its purpose.
Think of it as a pointer to a readonly int. You can point to any int you want, but it's going to be readonly no matter what.
You might use this to loop through an array of type const int, for example.

Use a const int* const pointer to point to an int

I don't understand something here. In the following code I have defined an integer and a constant integer.
I can have a constant pointer (int* const) point to an integer. See the fourth line of code.
The same constant pointer (int* const) can not point to a constant integer. See the fifth line.
A constant pointer to a const (const int* const) can point to a constant integer. That's what I would expect.
However, the same (const int* const) pointer is allowed to point to a non constant integer. See the last line. Why or how is this possible?
int const constVar = 42;
int variable = 11;
int* const constPointer1 = &variable;
int* const constPointer2 = &constVar; // not allowed
const int* const constPointer3 = &constVar; // perfectly ok
const int* const constPointer4 = &variable; // also ok, but why?
int const constVar = 42; // this defines a top-level constant
int variable = 11;
int *const constPointer1 = &variable;
int *const constPointer2 = &constVar; // not allowed because you can change constant using it
const int *const constPointer3 = &constVar; // perfectly ok. here you can't change constVar by any mean. it is a low level constant.
const int *const constPointer4 = &variable; // also ok, because it says you can't change the value using this pointer . but you can change value like variable=15 .
*constPointer4=5; //you get error assignment of readonly location.because that pointer is constant and pointing to read only memory location.
You can always decide not to modify a non-const variable.
const int* const constPointer4 = &variable;
Just parse the definition: constPointer4 is a const (i.e you can't change what it is pointing to anymore) pointer to a const int (i.e. variable). This means that you can't modify variable through constPointer4, even though you can modify variable by other means.
THe other way around (accessing a const variable through a non-const pointer), you would need a const_cast.
Why is a pointer to const useful? It allows you to have const member functions in classes where you can guarantee to users that that member function does not modify the object.
const has less access rights than non const, thats why it is allowed. You will not be able to change "variable" through the pointer but that is not breaking any rules.
variable = 4; //ok
*constPointer4 = 4; //not ok because its const
You use this "const pointer to non const variable" situation a lot when calling functions.
void f(const int * const i)
{
i=4; //not ok
}
f(&variable);
A pointer to a const object cannot be used to modify that object. It doesn't matter whether someone else can modify it; it just can't be done through that pointer.
int i; // modifiable
const int *cip = &i; // promises not to modify i
int *ip = &i; // can be used to modify i
*cip = 3; // Error
*ip = 3; // OK
Line # 4
int* const constPointer2 = &constVar;
Here it should not be allowed because int* const part of the "int* const constPointer2" means that the pointer is contant and then when you go ahead and assign it to &constVar