Updating const variable value in C++
Modifying a const
value through any mechanism (including casting away const-ness) results in "undefined behavior" (UB) which means that you cannot reason about the behavior of the program. The compiler is allowed to assume that const
values will never change. Based on that assumption, the compiler might:
- Store
const
values in read-only memory pages; attempting assignment through the pointer would then cause an access violation. - Inline the
const
value wherever it is used. Because you take a pointer, the compiler will likely also emit some kind of storage for the value (on the stack most likely) so that it has a memory location that can be pointed to, but modifying this value will not cause the inlined values to change. - Something else, possibly including both of the above.
Which one it does can depend on the optimization level selected.
A program that assigns to a const
value is effectively "nonsense" and has no meaningful interpretation.
Note that this is UB in both C and C++. You just need to twist the C++ compiler's arm a bit more to get the code to compile (int *ptr = const_cast<int *>(&var);
). The C standard permits implicit discarding of a const
qualifier in some contexts; the C++ standard is a lot more strict about this.
Most C compilers will emit a warning on int *ptr = &var;
regarding discarding of the const
qualifier. I would strongly recommend compiling with all warnings enabled and converted to errors (-Wall -Werror
on gcc). That would cause the C compiler to also refuse to compile this code.
Note that casting const
away from a pointer (or reference) and assigning to the target is not UB when the value was not declared const
:
// non-const value
int x = 10;
// take a pointer to x and store it in a pointer-to-const
const int *y = &x;
// cast away the const-ness of the pointer target
int *z = const_cast<int *>(y);
// this is fine; z points at x which is not const
*z = 5;
However, if you find yourself needing const_cast
then most likely you are either (1) doing some crazy template metaprogramming, or (2) approaching the problem wrong.
Can we modify the value of a const variable?
The author's point is that declaring a variable with register
storage class prevents you from taking its address, so it can not be passed to a function that might change its value by casting away const
.
void bad_func(const int *p) {
int *q = (int *) p; // casting away const
*q = 42; // potential undefined behaviour
}
void my_func() {
int i = 4;
const int j = 5;
register const int k = 6;
bad_func(&i); // ugly but allowed
bad_func(&j); // oops - undefined behaviour invoked
bad_func(&k); // constraint violation; diagnostic required
}
By changing potential UB into a constraint violation, a diagnostic becomes required and the error is (required to be) diagnosed at compile time:
c11
5.1.1.3 Diagnostics
1 - A conforming implementation shall produce at least one diagnostic message [...] if a preprocessing translation unit or translation unit
contains a violation of any syntax rule or constraint, even if the behavior is also explicitly
specified as undefined or implementation-defined.6.5.3.2 Address and indirection operators
Constraints
1 - The operand of the unary
&
operator shall be [...] an lvalue that designates an object that [...] is
not declared with theregister
storage-class specifier.
Note that array-to-pointer decay on a register
array object is undefined behaviour that is not required to be diagnosed (6.3.2.1:3).
Note also that taking the address of a register
lvalue is allowed in C++, where register
is just an optimiser hint (and a deprecated one at that).
changing const value in C
It's allowed because you have overruled the constness of ptr1 by casting it to a non-const pointer. This is why casts can be very dangerous.
Note that some compilers, such as GCC, will not allow you to cast away const status like this.
change the values of const variable and static const variable in c language
Both programs execute statements int *b; b = &a; *b = 200;
which invokes undefined behaviour because a
is a const int
and therefore shouldn't be modified. There is no right answer (expected output) — both crashing and not crashing are acceptable results, as is printing 10
or 200
(or lemons and oranges
— though that's a little unlikely to happen).
Don't execute anything that causes undefined behaviour!
Your compiler should be complaining; heed its warnings. If it isn't complaining, get a better compiler.
The difference is that the static const int a = 10;
variable is placed in a readonly segment (probably part of the text segment, though it really doesn't matter), so the system can spot when you write to it and causes the crash. On the other hand, const int a = 10;
is placed on the stack, and the stack is modifiable, so you don't get a crash.
Is it possible to change value of a constant variable via reinterpret_cast?
The thing that is interesting for me, how the program output is 5, while debugger clearly indicates the value of a is 10?
This depends entirely on the compiler. It could output 10, 5, crash, ... because it is undefined behavior.
If you want to know why the output of the binary created by a particular compiler has a certain result for undefined behavior, you have to look at the generated output of the compiler. This can be done using e.g. godbolt.org
For your code the output gcc (11.2) generates is:
push rbp
mov rbp, rsp
sub rsp, 16
mov BYTE PTR [rbp-9], 5
lea rax, [rbp-9]
mov QWORD PTR [rbp-8], rax
mov rax, QWORD PTR [rbp-8]
mov BYTE PTR [rax], 10
mov esi, 5
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(unsigned int)
mov esi, OFFSET FLAT:_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))
mov eax, 0
leave
ret
Here you can see that the compiler correctly assumes that the value of a
will not change. And replaces std::cout << unsigned(a) << std::endl;
with std::cout << unsigned(5) << std::endl;
:
mov esi, 5
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(unsigned int)
If you remove the const
from a
the output is:
movzx eax, BYTE PTR [rbp-9]
movzx eax, al
mov esi, eax
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(unsigned int)
Related Topics
Connected Components in Opencv
Template Within Template: Why "'>>' Should Be '> >' Within a Nested Template Argument List"
Construct Path for #Include Directive with MACro
Does Returning a Local Variable Return a Copy and Destroy the Original(Nrvo)
Error: Invalid Initialization of Non-Const Reference of Type 'Int&' from an Rvalue of Type 'Int'
How to Initialize a Static Const Member in C++
Loop Unrolling to Achieve Maximum Throughput with Ivy Bridge and Haswell
Opencv 2.3 C - How to Isolate Object Inside Image
Why Static Variable Needs to Be Explicitly Defined
Check If a Variable Type Is Iterable
Why There Is No Placement Delete Expression in C++
Std::List<>::Sort()' - Why the Sudden Switch to Top-Down Strategy
Constant References with Typedef and Templates in C++
Variable Declarations in Header Files - Static or Not