I wondered, if the const
qualifier in C is the attribute of the pointer or the memory area?
If I do something like:
struct S { int data; }
struct CS { const int data; }
char *p = malloc(100);
struct S *s = p+10;
struct CS *cs = p+10;
Can I access the same memory area by CS
for reading and S
for writing, without these two ever knowing of each other?
Or does the const
mark that exact memory area to be const
?
Q&A: No, I can't just "try it out", because I don't know when and how this difference can come in play. All the experiences I had so far point to the const
being strictly a variable attribute. Is that so, or have I just missed that one example that proves me wrong?
const
has two effects. One is when an object is defined with const
, that memory is “permanently tainted”: Any attempt to modify the object’s memory is not defined by the C standard. The C implementation may make that memory read-only. That effect is not relevant to your example.
The second is that const
qualifies a type and requires diagnostic messages when the type is “misused”. For example if a const int *
is assigned to an int *
(with no cast), the compiler must issue a diagnostic. Or if any value is assigned to a const int
, the compiler must issue a diagnostic. This also is not an issue in your example.
There is a third effect, although it is a property of all aspects of types, not just the const
qualifier: It makes one type different from another. And then the types are subject to the aliasing rule in C 2024 6.5. And that controls the answer to your question:
Can I access the same memory area by
CS
for reading andS
for writing, without these two ever knowing of each other?
The C standard is a bit hazy on this. Let’s consider two cases. The first is you write to s
with *s = (struct S) { 4 };
and read from cs
with struct CS foo = *cs;
. The write is defined; without going into detail, the rules of effective type and aliasing say you may write to dynamically allocated memory with any type. Let’s consider the read.
Once dynamically allocated memory is written with a non-character type, that type becomes its effective type for subsequent reads. So, *s = (struct S) { 4 };
gives the memory an effective type of struct S
.
The aliasing rules say that accessing this memory using struct CS
is not defined, because they say an object shall be accessed only with one of these types:
struct CS
is not compatible with struct S
]struct CS
is not a version of structure S
or of a compatible type],struct CS
is not a signed or unsigned type because those are integer types],CS
, const int
, is not compatible with any of the aforementioned],CS
is not a character type].Therefore, struct CS foo = *cs;
is not defined by the C standard.
The second case is you write to s
with s->data = 4;
and you read from cs
with int foo = cs->data;
. In this case, the effective type of the memory is int
,1 and we read from it with const int
. This is allowed by the aliasing rules because one of the allowed types is:
const int
is compatible with const int
, which is a qualified version of the effective type, int
].1 This is hazy because the C standard does not explicitly address the type when a structure is written to dynamically allocated memory by individually writing all of its members. Does that combine to form a structure object in that memory? If memory is written as a structure, giving it an effective type of that structure, can we individually access its members? There is probably a language-lawyer question on this elsewhere on Stack Overflow.