"Allowed" is the opposed of "prevented", but it's also the opposite of "forbidden". You've seen that modifying your const object isn't prevented, but that doesn't exactly mean it's allowed.
Modifying a const object isn't "allowed" in the sense of being "permitted". The behaviour of your program is not defined by the standard (see 6.7.3/5). It just so happens that on your implementation, on that run, you saw the value 3. On another implementation or on another day, you might see a different result.
However, it's not "prevented", because with the way C casts work, detecting it at compile time is a halting problem. Detecting it at runtime requires extra checks at all memory accesses. The standard is designed not to impose a lot of overhead on implementations.
The reason casting away const is supported at all, is because if you have a const pointer to a non-const object, the language allows you (in both senses) to modify that object. To do so you need to get rid of the const qualifier. The consequence of this is that programmers can also discard const qualifiers from pointers to objects which actually are const.
Here's a (slightly silly) example of code which discards a const qualifier for that reason:
typedef struct {
const char *stringdata;
int refcount;
} atom;
// returns const, because clients aren't allowed to directly modify atoms,
// just read them
const atom *getAtom(const char *s) {
atom *a = lookup_in_global_collection_of_atoms(s);
if (a == 0) {
// error-handling omitted
atom *a = malloc(sizeof(atom));
a->stringdata = strdup(s);
a->refcount = 1;
insert_in_global_collection_of_atoms(a);
} else {
a->refcount++;
}
return a;
}
// takes const, because that's what the client has
void derefAtom(const atom *a) {
atom *tmp = (atom*)a;
--(tmp->refcount);
if (tmp->refcount == 0) {
remove_from_global_collection_of_atoms(a);
free(atom->stringdata);
free(atom);
}
}
void refAtom(const atom *a) {
++(((atom*) a)->refcount);
}
It's silly because a better design would be to forward-declare atom
, to make pointers to it completely opaque, and provide a function to access the stringdata. But C doesn't require that you encapsulate everything, it allows you to return pointers to fully-defined types, and it wants to support this kind of const usage to present a read-only view of an object that's "really" modifiable.