Why can you kind of cheat compiler this way:
const int a = 5;
*((int*)&a)=5; // VC/armcc does not complain
when above is "abridged" equivalent of this:
const int *ptr2const = &a;
int *ptr = ptr2const; // as expected error is raised here
*ptr = 5;
Casting is your way of telling the compiler "I know what I'm doing", so it doesn't complain. Unfortunately, in this instance, you will invoke undefined behaviour.
C-style casts allow you to cast away constness like in your example. In C++, you would normally use the new style casts such as static_cast<>, which don't allow you to cast away constness. Only const_cast<> allows you to do that.
To be equivalent, the 2nd line of the 2nd snippet
int *ptr = ptr2const; // as expected error is raised here
should be written as
int *ptr = (int *)ptr2const;
Because C throws away a lot of type safety in order to gain a lot of speed instead. It cannot prevent you from doing incorrect things. It may try to warn you that you are doing incorrect things, but you can always work around the compiler if that is your goal.
Convert your constant to a string and you may find that while the compiler will let you cast away the const (inadvisable though it may be), the linker may put the constant string in read-only memory leading to a runtime crash.
C-style casts, such as (int*) are equivalent to C++ const_cast in their ability to cast away constness, so you can side-step const-correctness by using them, although such use is unrecommended (can lead to undefined behaviour).
int main()
{
const int x = 1;
(int&)x = 2;
std::cout << x << std::endl;
}
On my system, the above writes 1 to stdout. You might experience different behaviour.
On the other hand...
void foo(const int& x)
{
(int&)x = 2;
}
int main()
{
int x = 1;
foo(x);
std::cout << x << std::endl;
}
This writes 2 for me. The difference is that the const used in foo is const as a type qualifier, while in the main in the first example it was used as a storage class. It's not always easy to see whether a variable was declared with const as a storage class, so it's best not to rely on a const_cast or C-style cast to cast away const.
It's best just to use static_cast in most situations as this will warn you at compile time of any suspect behaviour in the form of a compile error.
This only "works" because the variable is local, and implementations have no way to enforce const-ness on a local (automatic) variable in general (at least not if the address of the variable is ever taken). But as far as the language specification is concerned, this is in the realm of undefined behavior.
If you try this on a global/static variable, you'll quickly find that most implementations can and do enforce const by putting the variable in read-only memory which can be shared between multiple instances of your program.
Related
As we know the value of constant variable is immutable. But we can use the pointer of constant variable to modify it.
#include <iostream>
int main()
{
const int integer = 2;
void* tmp = (void*)&integer;
int* pointer = (int*)tmp;
(*pointer)++;
std::cout << *pointer << std::endl;
std::cout << integer << std::endl;
return 0;
}
the output of that code is:
3
2
So, I am confusing what i modified on earth? what does integer stand for?
Modifying consts is undefined. The compiler is free to store const values in read only portions of memory and throw error when you try to change them (free to, not obliged to).
Undefined behavior is poor, undesirable and to be avoided. In summary, don't do that.
PS integer and pointer are variable names in your code, tho not especially good names.
You have used unsafe, C-style casts to throw away the constness. C++ is not an inherently safe language, so you can do crazy stuff like that. It does not mean you should. In fact, you should not use C-style casts in C++ at all--instead use reinterpret_cast, const_cast, static_cast, and dynamic_cast. If you do that, you will find that the way to modify const values is to use const_cast, which is exactly how the language is designed.
This is an undefined behavior. The output you get is compiler dependent.
One possible explanation for this behavior is as follows.
When you declares integer as a constant, and use it in an expression, a compiler optimization and substitute it with the constant literal you have assigned to it.
But, the actual content of the memory location pointed by &integer is changed. Compiler merely ignore this fact because you have defined it as a constant.
See Const Correctness in C++. Give some attention to the assembler output just above the 'The Const_cast Operator' section of this page.
You're wading into Undefined Behavior territory.
If you write
void* tmp = &integer;
the compiler would give you an error. If you wrote good C++ code and wrote
void* tmp = static_cast<void*>(&integer);
the compiler would still give you an error. But you went ahead and used a C-style unprotected cast which left the compiler no option but to do what you told it.
There are several ways the compiler could deal with this, not least of which:
It might take the address of a location in the code segment where the value was, e.g., being loaded into a register.
It might take the address of a location of a similar value.
It might create a temporary by pushing the value onto the stack, taking the address of the location, and then popping the stack.
You would have to look at the assembly produced to see which variant your compiler prefers, but at the end of the day: don't do it it is undefined and that means next time you upgrade your compiler or build on a different system or change optimizer settings, the behavior may well change.
Consider
const char h = 'h';
const char* hello = "hello";
const unsigned char num = 2 * 50 + 2 * 2; // 104 == 'h'
arg -= num; // sub 104, eax
char* ptr = (char*)(&h);
The compiler could choose to store an 'h' specially for the purpose of 'ptr', or it could choose to make 'ptr' point to the 'h' in hello. Or it could choose to take the location of the value 104 in the instruction 'sub 104, eax'.
The const key word is just a hint for compiler. Compiler checks whether a variable is const or not and if you modify a const variable directly, compiler yield a wrong to you. But there is no mechanism on variable storage to protect const variables. So operating system can not know which variable is const or not.
According to the c++ grammar, const int* const p means that what p points to and it' value can't be rewritten.But today I found that if I code like this:
void f(const int* const p)
{
char* ch = (char*) p;
int* q = (int*) ch;
(*q) = 3; //I can modify the integer that p points to
}
In this condition,the keyword "const" will lose it's effect.Is there any significance to use "const"?
You are casting away the constness here:
char* ch = (char*) p;
Effectively, you are saying "I know what I am doing, forget you are const, I accept the consequences." C++ allows you to do stuff like this because sometimes it can be useful/necessary. But it is fraught with danger.
Note that if the argument passed to the function were really const, then your code would result in undefined behaviour (UB). And you have no way of knowing from inside the function.
Note also that in C++ it is preferable to make your intent clear,
int* pi = const_cast<int*>(p);
This makes it clear that your intention is to cast away the const. It is also easier to search for. The same caveats about danger and UB apply.
Your example will crash the app if const int* const p points to a compile time constant, when casting away constancy you need to be sure what you are doing.
C++ is a language for adults, extra power will never be sacrificed for ephemeral safety, it is the code author's choice whether to stay in a safe zone or to use cast operators and move one step closer to C/asm.
C/C++ will let you do many things that allow you to 'hurt' yourself. Here, casting away the const of p is "legal" because the compiler assumes you know what you are doing. Whether this is good coding style or not is another matter.
When you do something like this, you assume responsibility for any side effects and issues it could create. Here, if the memory pointed to in the parameter is static memory, the program will likely crash.
In short, just because you can do something doesn't mean it is a good idea.
The const keyword is a way to use the compiler to catch programming mistakes, nothing more. It makes no claims about the mutability of memory at runtime, only that the compiler should shout at you if you directly modify the value (without using C-style casts).
A C-style cast is pretty much a marker saying 'I know what I'm doing here'. Which in most instances is false.
Here you change the type of the pointer. Using such a cast (C-type) cast you can change it to any possible pointer with no problem.
The same way you can use c++ cast: const_cast<...>(...):
int* p_non_const = const_cast<int*>(p);
In this case (I hope) you see immediately what is happening - you simply rid of the constness.
Note that in your program you also don't need temprorary variable ch. You can do it directly:
int* q = (int*) p;
In principle you should not use such a cast, because correctly designed and written program doesn't need it. Usually the const_cast is used for quick and temporary changes in the program (to check something) or to use a function from some unproperly designed library.
Which is safer to use?
int main()
{
const int i=5;
int *ptr;
ptr=(int*)&i; <------------------- first
ptr=const_cast<int*>(&i); <-------------------Second
return 0;
}
It's safer in the sense that you won't get a cast that's something other than just removing const:
int main()
{
const char i=5;
int *ptr;
ptr=(int*)&i; // the compiler won't complain
ptr=const_cast<int*>(&i); // will fail, since `i` isn't an int
return 0;
}
which doesn't necessary mean that the const_cast<> is safe:
const int i=5;
int main()
{
int const& cri(i);
int& ri = const_cast<int&>(cri); // unsafe
ri = 0; // will likely crash;
return 0;
}
They are entirely equivalent, except that C-style casts present more of a maintenance headache over the const_cast. If the code were frozen in time, they would be identical. The Standard says that the C-style cast may devolve to static, reinterpret, or const cast or a combination of the three, or a strange funky cast that can access private bases for some reason. The point is, in this use case it is exactly equivalent to const_cast.
I'm not sure about safety - I'm sure someone is more well-versed in this than I am - but C++-style casts are part of the language standard and should always be preferred over C-style casts (as a matter of both style as well as readability).
To amend my answer, it appears that C++-style casts are checked by the compiler whereas C-style casts fail at runtime; in that regard, C++-style casts are definitely safer.
Neither is safer than the other. In both cases undefined behavior will occur should you modify the value through one of the pointers that have been casted. const_cast has the benefit of doing only what you want and expresses it clearly, while the C style cast could be everything and is not sensitive to the actual type of its argument.
It's safer, but in a different way than you imagine.
It's safer because you explicitly state you're casting away constness.
When someone sees your code, they think - "ok, here's a const_cast, this argument must have been const. Let's take a closer look at this", whereas a regular cast just gets lost in the back of the mind when reading big chunks of code.
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.
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.