Does const on value different const on array - c++

It seems that const on array will cause the memory used to store the array mark read-only in memory. But why const on int won't do the same thing?
Code is here:
int main(int argc, const char * argv[])
{
const int vv = 10 ;
int * p = (int *)&vv ;
*p = 5 ; // work well
const int aa[3] = {11, 12, 13} ;
int * pp = (int *)&aa[1] ;
*pp = 100 ; // EXC_BAD_ACCESS
return 0;
}

Both the first and the second, as stated in §7.1.6.1/4, results in undefined behavior:
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.
This is, in fact, one of those cases where a C++-style cast (like static_cast), except for const_cast, would have warned you.
In the C standard the same reference is made at §6.7.3/5:
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.

Any attempt to modify data that is const results in undefined behaviour (UB). That means that your code might seem to "work well", but it cannot be relied on for anything.
Both your examples are UB.

If you get a bad access on the second access it means your compiler doesn't copy the initializer list to aa but have aa point to the process's const data section (e.g. where the string literals are stored).
As others have pointed out, changing a const produces undefined behavior. In Visual Studio, your code will not produce any errors.
The problem surfaces when multiple references to the same const object (or array in C) occur. Changing one will change all of them and only in optimized builds. This sort of bug is hard to track.

look ISO/IEC 9899 TC3 ->§6.7.3 part 5 there is said:
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. If an attempt is made to refer to an object defined with a volatile-qualified type through use of an lvalue with non-volatile-qualified type, the behavior is undefined.
So I guess this is clear enough for C, isn't it?

Your first part of code is pretty correct. You are modifying the value stored at location using its reference. This is defined behavior. The compiler checks this variable won't be on left side of an expression.
Use the following to keep value pointed by it to be constant.
const int * p = (int *)&vv

Related

Can contents of a `const vector` be modified w/o UB?

A const vector can't be modified as it's a const object. So inserts, appends, erases, are not allowed. However, its contents are not part of that object but are owned by it. As a similar example:
int* const p = new int[10]{1,2,3,4};
p is a const object that owns non-const data which can be modified: p[1]=5;
Vector's operator[] is conditioned on whether vector is const and if so returns a const int& But if the underlying value wasn't const then a const cast removing const should be legal.
To test this I wrote the following program:
#include <vector>
constexpr int foo()
{
const std::vector<int> v{ 1,2,3 };
const int a[3]{ 1,2,3 };
*const_cast<int*>(&v[1]) = 21;
// However, this should fail and does on GCC and CLANG
//*const_cast<int*>(&a[1]) = 21;
return v[1];
}
int main()
{
constexpr int sb21 = foo();
const std::vector<int> v{ 1,2,3 };
*const_cast<int*>(&v[1]) = 21;
return v[1] + sb21;
}
compiler explorer
MSVC, CLANG, and GCC all compile and execute.
The code evaluates a constexpr function at compile time. Compilers are supposed to produce compile time errors on UB. For comparison if the array, which contains const elements, is uncommented, Clang and GCC both produce errors as expected. However, MSVC does not which appears to be a bug.
Use case is having a fixed size vector that can't be structurally altered but can have contents updated.
std::vector<T> uses std::allocator<T> and so long as the library implementation of vector doesn't use small sizes like std::string's short string optimization then this should be defined behavior.
Here's an example showing how a const std::string exhibits UB for small strings that are stored within the object while longer allocated ones do not:
#include <string>
consteval int foo()
{
const std::string v{ "1234" };
//const std::string v{ "123412341234123412341234" };
*const_cast<char*>(&v[1]) = 'A';
return v[1];
}
int main()
{
return foo();
}
Compiler Explorer
Is this defined behavior or are the compilers not flagging UB?
But if the underlying value wasn't const then a const cast removing const should be legal.
This is the weak point of your argument. It's not the underlying value that matters, but the owned object. If the owned object is not a const object, then removing the cast should be legal. However, can you prove that the owned object is not const?
I believe you cannot. Take your own example – a vector containing three ints. Hypothetically, suppose each int is 4 bytes, so the total data is 12 bytes. Also suppose the size of a vector is 24 bytes (allowing 8 bytes for each of a pointer, size, and capacity). It would not be unreasonable to optimize a bit and store the three ints in the vector itself, along with a flag to say that the data is inside the vector instead of being dynamically allocated (a similar approach is used in short string optimization).
Now that we have the possibility that the data is inside the vector itself, we have the possibility that the data is part of a const object, because the vector is const. Casting away this constness to change a value is undefined behavior.
The bottom line is that if you do not own the object, you cannot know for sure how it was created. If the owner tells you it is const, then you have to treat it as const.
Compilers are supposed to produce compile time errors on UB.
The above statement is incorrect unless explicitly specified by the standard.
Can contents of a const vector be modified w/o UB?
No. Trying to modify a const variable leads to undefined behavior.
From dcl.type.cv:
Except that any class member declared mutable can be modified, any attempt to modify a const object during its lifetime results in undefined behavior.
(end quote)
Undefined behavior means anything1 can happen including but not limited to the program giving your expected output. But never rely(or make conclusions based) on the output of a program that has undefined behavior. The program may just crash.
So the output that you're seeing(maybe seeing) is a result of undefined behavior. And as i said don't rely on the output of a program that has UB. The program may just crash.
So the first step to make the program correct would be to remove UB. Then and only then you can start reasoning about the output of the program.
1For a more technically accurate definition of undefined behavior see this where it is mentioned that: there are no restrictions on the behavior of the program.
const_cast from const T& to T& is not undefined behavior unless the underlying data is itself const.
AFAICT, there is no difference between vector<int> and const vector<int> in that aspect: in both cases it is not UB to const_cast v[0] to int&.
I see other answers here that speculate otherwise, but I don't see any evidence of that in the standard.
See also:
Modifying element of const std::vector<T> via const_cast
Also see a similar discussion here:
const vector implies const elements?

const_cast VS mutable ? any difference?

From my understanding , mutable cancels the constness of a variable
Class A {
void foo() const {
m_a = 5;
}
mutable int m_a;
};
But also const_cast :
void print (char * str)
{
cout << str << endl;
}
int main () {
const char * c = "this is a line";
print ( const_cast<char *> (c) );
return 0;
}
So , what changes one from the other ?
Thanks
const_cast cannot cancel constness of an object. const_cast can only remove constness from an access path to an object. Access path is a pointer or a reference to an object. Removing the constness from the access path has absolutely no effect on the object itself. Even if you use const_cast to remove the constness of the access path, that still does not necessarily give you the permission to modify the object. Whether you can do it or not still depends on the object itself. If it is const, you are not allowed to modify it and any attempts to do so will result in undefined behavior.
For example, this illustrates the intended use of const_cast
int i = 5; // non-constant object
const int *p = &i; // `p` is a const access path to `i`
// Since we know that `i` is not a const, we can remove constness...
int *q = const_cast<int *>(p);
// ... and legally modify `i`
*q = 10;
// Now `i` is 10
The only reason the above is legal and valid is the fact that i is actually a non-constant object, and we know about it.
If the original object was really constant, then the above code would produce undefined behavior:
const int j = 5; // constant object
const int *p = &j; // `p` is a const access path to `j`
int *q = const_cast<int *>(p); // `q` is a non-const access path to `j`
*q = 10; // UNDEFINED BEHAVIOR !!!
C++ language does not allow you to modify constant objects and const_cast is completely powerless here, regardless of how you use it.
mutable is a completely different thing. mutable creates a data filed that can be legally modified even if the containing object is declared const. In that sense mutable does allow you to modify [some designated parts of] constant objects. const_cast, on the other hand, can't do anything like that.
The difference is that const_cast can't cheat, but mutable is an exception to the rules.
On the first snippet m_a is mutable, and thus an exception to the rule that you can't modify data members on const member functions.
On the second snippet, const_cast tries to cheat, but really can't: while the type has changed, actual modification is not allowed: the string is truly const. Attempting to modify it would cause the program to exhibit undefined behaviour.
The difference is semantic - i. e. same generated code, same run-time results (constness is a purely compile-time construct anyway), but the two constructs convey a slightly different meaning.
The idea is that you use mutable for variables that are in the class, but don't constitute the state of the object. The classic example is the current position in a blob object. Navigating in the blob does not count as "modifying" the blob in a way that matters. By using mutable, you're saying "this variable may change, but the object is still the same". You're stating that for this particular class, const-ness does not mean "all variables are frozen".
const_cast, on the other way, means that you're violating existing const correctness and hope to get away with it. Probably because you're working with a 3rd party API that does not respect const (e. g. an old school C-based one).
Simply put, declaring a member variable as mutable makes it write-accessible from any constant method of that class without any other special syntax. const_cast on the other hand has to be performed whenever you want write access to an otherwise constant variable, and that variable doesn't even have to be a class member.
Unless you want to explicitly allow write access to a member variable, using const_cast in every case of violation of const correctness is preferred, if only to clearly state your intentions.
On a side note, const_cast can also be used to add or remove the volatile modifier.

const_cast c++ did not work for me

trying to understand the usage of const_cast. Code like the following:
const char* text="bb";
(const_cast<char&>(*text))='a';
cout<<*text;
...generates a runtime error.
Another question, in memory, how does the runtime (it) know that this area is const or not, what kind of flag is this ?
That code invokes undefined behaviour; it is not valid to write to a string literal (nor indeed to any const object).
The C++ standard does not define how this should fail (or even that it must fail). But on a typical platform, it will be up to the OS and the underlying hardware to detect the problem. The storage for "bb" will typically be in a dedicated section of the executable, which is marked as read-only. See e.g. http://en.wikipedia.org/wiki/Memory_protection.
However, there are uses of const_cast that don't invoke undefined behaviour. e.g.:
int x = 5; // Not a const object
const int *p = &x;
int *q = const_cast<int *>(p);
*q = 6; // This is ok
The string might be put in static memory. So it is an undefined behaviour.
Try this
char t[]="bb";
const char* text = t;
(const_cast<char&>(*text))='a';
cout<<*text;
You can only const_cast something which you know is not really const. In this case, even if text is const, we know that it points to t which is not const. Hence we can safely cast away the const.
Generally speaking, the run-time doesn't know whether a particular variable is actually const. If you cast away const-ness, you get undefined behavior if you end up writing to a variable defined as const (as opposed to a normal variable to which you happen to have a const pointer/reference).
If they wanted to mandate that the run-time "know" about things being const, then they'd probably prescribe specific behavior (e.g., throwing a particular exception) when/if you write to a const variable. Some systems would support that quite easily -- but others wouldn't, so a specific response isn't required.

const cast to a global var and program crashed (C++)

int main()
{
const int maxint=100;//The program will crash if this line is put outside the main
int &msg=const_cast<int&>(maxint);
msg=200;
cout<<"max:"<<msg<<endl;
return 0;
}
The function will run ok if the 'const int maxint=100;' definition is put inside the main function but crash and popup a error message said "Access Violation" if put outside.
Someone says it's some kind of 'undefined behavior', and i want to know the exact answer and how i can use the const cast safely?
They are correct, it is undefined behaviour. You're not allowed to modify the value of a const variable, which is the danger of casting away the constness of something: you better know it's not really const.
The compiler, seeing that maxint is const and should never be modified, doesn't even have to give it an address. It can just replace all the uses of maxint with 100 if it sees fit. Also it might just put the constant in to a portion of memory that is read-only, as Matteo Italia points out, which is probably what's happening for you. That's why modifying it produces undefined behaviour.
The only way you can safely cast away the constness of a variable is if the variable is not actually const, but the const qualifier was added to a non-const variable, like this:
int f(const int& x) {
int& nonconst = const_cast<int&>(x);
++nonconst;
}
int blah = 523;
f(blah); // this is safe
const int constblah = 123;
f(constblah); // this is undefined behaviour
Think about this example, which compiles perfectly:
int f(const int& x) {
int& nonconst = const_cast<int&>(x);
++nonconst;
}
int main() {
f(4); // incrementing a number literal???
}
You can see how using const_cast is pretty dangerous because there's no way to actually tell whether a variable is originally const or not. You should avoid using const_cast when possible (with functions, by just not accepting const parameters).
Modifying an object that is const (with the exception of mutable members) results in undefined behavior (from the C++03 standard):
7.1.5.1/4 "The cv-qualifiers"
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.
The above undefined behavior is specifically called out in the standard's section on const_cast:
5.2.11/7 "Const cast"
[Note: Depending on the type of the object, a write operation through
the pointer, lvalue or pointer to data member resulting from a
const_cast that casts away a const-qualifier68) may produce undefined
behavior (7.1.5.1). ]
So, if you have a const pointer or reference to an object that isn't actually const, you're allowed to write to that object (by casting away the constness), but not if the object really is const.
The compiler is permitted to place const objects in read-only storage, for example. It doesn't have to though, and apparently doesn't for your test code that doesn't crash.
You are only allowed to cast away constness of an object which is known not to be const. For example, an interface may pass objects through using a const pointer or a const reference but you passed in an object which isn't const and want/need to modify it. In this case it may be right thing to cast away constness.
On the other hand, casting away constness of an object which was const all the way can land you in deep trouble: when accessing this object, in particular when writing to it, the system may cause all kinds of strange things: the behavior is not defined (by the C++ standard) and on a particular system it may cause e.g. an access violation (because the address of the object is arranged to be in a read-only area).
Note that despite another response I saw const objects need to get an address assigned if the address is ever taken and used in some way. In your code the const_cast<int&>(maxint) expression essentially obtains the address of your constant int which is apparently stored in a memory area which is marked to be read-only. The interesting aspect of your code snippet is that it is like to apparently work, especially when turning on optimization: the code is simple enough that the compiler can tell that the changed location isn't really used and doesn't actually attempt to change the memory location! In this case, no access violation is reported. This is what apparently is the case when the constant is declared inside the function (although the constant may also be located on the stack which typically can't be marked as read-only). Another potential outcome of your code is (independent of whether the constant is declared inside the function or not) is that is actually changed and sometimes read as 100 and in other contexts (which in some way or another involve the address) as 200.

const casting an int in a class vs outside a class

I read on the wikipedia page for Null_pointer that Bjarne Stroustrup suggested defining NULL as
const int NULL = 0;
if "you feel you must define NULL." I instantly thought, hey.. wait a minute, what about const_cast?
After some experimenting, I found that
int main() {
const int MyNull = 0;
const int* ToNull = &MyNull;
int* myptr = const_cast<int*>(ToNull);
*myptr = 5;
printf("MyNull is %d\n", MyNull);
return 0;
}
would print "MyNull is 0", but if I make the const int belong to a class:
class test {
public:
test() : p(0) { }
const int p;
};
int main() {
test t;
const int* pptr = &(t.p);
int* myptr = const_cast<int*>(pptr);
*myptr = 5;
printf("t.p is %d\n", t.p);
return 0;
}
then it prints "t.p is 5"!
Why is there a difference between the two? Why is "*myptr = 5;" silently failing in my first example, and what action is it performing, if any?
First of all, you're invoking undefined behavior in both cases by trying to modify a constant variable.
In the first case the compiler sees that MyNull is declared as a constant and replaces all references to it within main() with a 0.
In the second case, since p is within a class the compiler is unable to determine that it can just replace all classInstance.p with 0, so you see the result of the modification.
Firstly, what happens in the first case is that the compiler most likely translates your
printf("MyNull is %d\n", MyNull);
into the immediate
printf("MyNull is %d\n", 0);
because it knows that const objects never change in a valid program. Your attempts to change a const object leads to undefined behavior, which is exactly what you observe. So, ignoring the undefined behavior for a second, from the practical point of view it is quite possible that your *myptr = 5 successfully modified your Null. It is just that your program doesn't really care what you have in your Null now. It knows that Null is zero and will always be zero and acts accordingly.
Secondly, in order to define NULL per recommendation you were referring to, you have to define it specifically as an Integral Constant Expression (ICE). Your first variant is indeed an ICE. You second variant is not. Class member access is not allowed in ICE, meaning that your second variant is significantly different from the first. The second variant does not produce a viable definition for NULL, and you will not be able to initialize pointers with your test::p even though it is declared as const int and set to zero
SomeType *ptr1 = Null; // OK
test t;
SomeType *ptr2 = t.p; // ERROR: cannot use an `int` value to initialize a pointer
As for the different output in the second case... undefined behavior is undefined behavior. It is unpredictable. From the practical point of view, your second context is more complicated, so the compiler was unable to prefrom the above optimization. i.e. you are indeed succeeded in breaking through the language-level restrictions and modifying a const-qualified variable. Language specification does not make it easy (or possible) for the compilers to optimize out const members of the class, so at the physical level that p is just another member of the class that resides in memory, in each object of that class. Your hack simply modifies that memory. It doesn't make it legal though. The behavior si still undefined.
This all, of course, is a rather pointless exercise. It looks like it all began from the "what about const_cast" question. So, what about it? const_cast has never been intended to be used for that purpose. You are not allowed to modify const objects. With const_cast, or without const_cast - doesn't matter.
Your code is modifying a variable declared constant so anything can happen. Discussing why a certain thing happens instead of another one is completely pointless unless you are discussing about unportable compiler internals issues... from a C++ point of view that code simply doesn't have any sense.
About const_cast one important thing to understand is that const cast is not for messing about variables declared constant but about references and pointers declared constant.
In C++ a const int * is often understood to be a "pointer to a constant integer" while this description is completely wrong. For the compiler it's instead something quite different: a "pointer that cannot be used for writing to an integer object".
This may apparently seem a minor difference but indeed is a huge one because
The "constness" is a property of the pointer, not of the pointed-to object.
Nothing is said about the fact that the pointed to object is constant or not.
The word "constant" has nothing to do with the meaning (this is why I think that using const it was a bad naming choice). const int * is not talking about constness of anything but only about "read only" or "read/write".
const_cast allows you to convert between pointers and references that can be used for writing and pointer or references that cannot because they are "read only". The pointed to object is never part of this process and the standard simply says that it's legal to take a const pointer and using it for writing after "casting away" const-ness but only if the pointed to object has not been declared constant.
Constness of a pointer and a reference never affects the machine code that will be generated by a compiler (another common misconception is that a compiler can produce better code if const references and pointers are used, but this is total bogus... for the optimizer a const reference and a const pointer are just a reference and a pointer).
Constness of pointers and references has been introduced to help programmers, not optmizers (btw I think that this alleged help for programmers is also quite questionable, but that's another story).
const_cast is a weapon that helps programmers fighting with broken const-ness declarations of pointers and references (e.g. in libraries) and with the broken very concept of constness of references and pointers (before mutable for example casting away constness was the only reasonable solution in many real life programs).
Misunderstanding of what is a const reference is also at the base of a very common C++ antipattern (used even in the standard library) that says that passing a const reference is a smart way to pass a value. See this answer for more details.