#include <stdio.h>
int main()
{
const int a = 12;
int *p;
p = &a;
*p = 70;
}
Will it work?
c++constantsundefined-behavior
#include <stdio.h>
int main()
{
const int a = 12;
int *p;
p = &a;
*p = 70;
}
Will it work?
As others mentioned, the cast removes the 'constness' of the destination as far as the expression is concerned. When you use a cast the compiler treats the expression according to the cast - as long as the cast itself it valid (and C-style casts are pretty much the big hammer). This is why you don't get an error or warning. You're essentially telling the compiler, "be quiet, I know what I'm doing, this is how you should treat things". In fact, casts are probably the #1 way for programmers to get the compiler to stop issuing warnings.
Your assignment expression may or may not be undefined behavior. It is permitted to cast away constness if the object actually pointed to is not const.
However, if the object pointed to is const, then you have undefined behavior.
void TestFunc(const void * const Var1, const float Var2)
{
*(float*)Var1 = Var2;
}
int
main(void)
{
float x = 1.0;
const float y = 2.0;
TestFunc( &x, -1.0); // well defined (if not particularly great style)
TestFunc( &y, -2.0); // undefined behavior
return 0;
}
You're treading dangerous waters...
In general (I'm sure there are exceptions), casting so that expressions treat objects as they really are is supported, well-defined behavior in C/C++.
This particular behavior is covered in the standards mostly by statements that modifying a const object through a cast (or something) that removes the const qualifier is undefined. The inference is that doing the same for a non-const object is not undefined. An example given in the C++ standard makes this clear.
C90 6.5.3 - Type Qualifiers (C99 6.7.3):
If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined.
C++ 7.1.5.1 The cv-qualifiers
A pointer or reference to a cv-qualified type need not actually point or refer to a cv-qualified object, but it is treated as if it does; a const-qualified access path cannot be used to modify an object even if the object referenced is a non-const object and can be modified through some other access path. [Note: cv-qualifiers are supported by the type system so that they cannot be subverted without casting (5.2.11). ]
Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.
...
[Example:
...
int i = 2; //not cv-qualified const int* cip; //pointer to const int cip = &i; //OK: cv-qualified access path to unqualified *cip = 4; //ill-formed: attempt to modify through ptr to const int* ip; ip = const_cast<int*>(cip); //cast needed to convert const int*to int* *ip = 4; //defined: *ip points to i, a non-const object const int* ciq = new const int (3); //initialized as required int* iq = const_cast<int*>(ciq); //cast required *iq = 4; //undefined: modifies a const object
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:
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
Constraints1 - 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).
Best Answer
It's "undefined behavior," meaning that based on the standard you can't predict what will happen when you try this. It may do different things depending on the particular machine, compiler, and state of the program.
In this case, what will most often happen is that the answer will be "yes." A variable, const or not, is just a location in memory, and you can break the rules of constness and simply overwrite it. (Of course this will cause a severe bug if some other part of the program is depending on its const data being constant!)
However in some cases -- most typically for
const static
data -- the compiler may put such variables in a read-only region of memory. MSVC, for example, usually puts const static ints in .text segment of the executable, which means that the operating system will throw a protection fault if you try to write to it, and the program will crash.In some other combination of compiler and machine, something entirely different may happen. The one thing you can predict for sure is that this pattern will annoy whoever has to read your code.