Why is it allowed to change a const variable using a pointer to it with memcpy?
This code:
const int i=5;
int j = 0;
memcpy(&j, &i, sizeof(int));
The question asks why. Here's why:
This is allowed because once you have a pointer to a memory address, the language does not know what it points to. It could be a variable, part of a struct, the heap or the stack, or anything. So it cannot prevent you from writing to it. Direct memory access is always unsafe and to be avoided if there's another way of doing it.
The const
stops you modifying the value of a const
with an assignment (or increment etc). This kind of mutation is the only operations it can guarantee you won't be able to perform on a const.
Another way to look at this is the division of the static context (i.e. at compile time) and the runtime context. When you compile a piece of code which may, for example, make an assignment to a variable, the language can say "that's not allowed, it's const" and that is a compilation error. After this, the code is compiled into an executable and the fact that it is a const
is lost. Variable declarations (and the rest of the language) is written as input to the compiler. Once it is compiled, the code isn't relevant. You can do a logical proof in your compiler to say that const
s aren't changed. The compiled program runs, and we know at compile time that we have created a program that doesn't break the rules.
When you introduce pointers, you have behaviour that can be defined at run-time. The code that you wrote is now irrelevant, and you can [attempt to] do what you want. The fact that pointers are typed (allowing pointer arithmetic, interpreting the memory at the end of a pointer as a particular type) means that the language gives you some help, but it can't prevent you from doing anything. It can make no guarantees, as you can point a pointer anywhere. The compiler can't stop you breaking the rules at run-time with code that uses pointers.
That said, pointers are the way we get dynamic behaviour and data structures, and are necessary for all but the most trivial code.
(The above is subject to lots of caveats, i.e. code heuristics, more sophisticated static analysis bus is broadly true of a vanilla compiler.)
The reason why is because the C language allows any pointer type to be implicitly casted to/from the type void*
. It is designed that way because void pointers are used for generic programming.
So a C compiler is not allowed to stop your code from compiling, even though the program invokes undefined behavior in this case. A good compiler will however give a warning as soon as you implicitly try to cast away a const qualifier.
C++ has "stronger typing" than C, meaning that it would require an explicit cast of the pointer type for this code to compile. This is one flaw of the C language that C++ actually fixed.
While 'officially' it's undefined in reality it's very much defined - you will change the value of the const variable. Which raises the question why it's const to begin with.
Attempt to modify the value of a const-qualified variable leads to an undefined behavior in C. You should not rely on your results, since anything can happen.
C11 (n1570), § 6.7.3 Type qualifiers
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.
Nothing force the compiler to produce a diagnostic message.
In fact, this qualifier has not enormous effects on the machine code. A const-qualified variable does not usually reside in a read-only data segment (obviously, not in your implementation, although it could be different on an other one).
The compiler can't tell easily what a pointer is pointing to in a given function. It is possible with some static analysis tools, which perform pointer-analysis. However, it is difficult to implement, and it would be stupid to put it in the standard.