I have this very simple test function that I'm using to figure out what's going on with const qualifier.
int test(const int* dummy)
{
*dummy = 1;
return 0;
}
This one throws me an error with GCC 4.8.3. Yet this one compiles:
int test(const int* dummy)
{
*(char*)dummy = 1;
return 0;
}
So it seems like the const qualifier works only if I use the argument without casting to other type.
Recently I've seen codes that used
test(const void* vpointer, ...)
At least for me, when I used void*, I tend to cast it to char* for pointer arithmetic in stacks or for tracing. How can const void* prevent subroutine functions from modifying the data at which vpointer is pointing?
const int *var;
const
is a contract. By receiving a const int *
parameter, you "tell" the caller that you (the called function) will not modify the objects the pointer points to.
Your second example explicitly breaks that contract by casting away the const qualifier and then modifying the object pointed by the received pointer. Never ever do this.
This "contract" is enforced by the compiler. *dummy = 1
won't compile. The cast is a way to bypass that, by telling the compiler that you really know what you are doing and to let you do it. Unfortunately the "I really know what I am doing" is usually not the case.
const
can also be used by compiler to perform optimization it couldn't otherwise.
Undefined Behavior note:
Please note that while the cast itself is technically legal, modifying a value declared as const
is Undefined Behavior. So technically, the original function is ok, as long as the pointer passed to it points to data declared mutable. Else it is Undefined Behavior.
more about this at the end of the post
As for motivation and use lets take the arguments of strcpy
and memcpy
functions:
char* strcpy( char* dest, const char* src );
void* memcpy( void* dest, const void* src, std::size_t count );
strcpy
operates on char strings, memcpy
operates on generic data. While I use strcpy as example, the following discussion is exactly the same for both, but with char *
and const char *
for strcpy
and void *
and const void *
for memcpy
:
dest
is char *
because in the buffer dest
the function will put the copy. The function will modify the contents of this buffer, thus it is not const.
src
is const char *
because the function only reads the contents of the buffer src
. It doesn't modify it.
Only by looking at the declaration of the function, a caller can assert all the above. By contract strcpy
will not modify the content of the second buffer passed as argument.
const
and void
are orthogonal. That is all the discussion above about const
applies to any type (int
, char
, void
, ...)
void *
is used in C for "generic" data.
Even more on Undefined Behavior:
Case 1:
int a = 24;
const int *cp_a = &a; // mutabale to const is perfectly legal. This is in effect
// a constant view (reference) into a mutable object
*(int *)cp_a = 10; // Legal, because the object referenced (a)
// is declared as mutable
Case 2:
const int cb = 42;
const int *cp_cb = &cb;
*(int *)cp_cb = 10; // Undefined Behavior.
// the write into a const object (cb here) is illegal.
I began with these examples because they are easier to understand. From here there is only one step to function arguments:
void foo(const int *cp) {
*(int *)cp = 10; // Legal in case 1. Undefined Behavior in case 2
}
Case 1:
int a = 0;
foo(&a); // the write inside foo is legal
Case 2:
int const b = 0;
foo(&b); // the write inside foo causes Undefined Behavior
Again I must emphasize: unless you really know what you are doing, and all the people working in the present and in the future on the code are experts and understand this, and you have a good motivation, unless all the above are met, never cast away the constness!!