Search code examples
cmemoryconstants

What is the const qualifier attached to in C: the memory area or the pointer?


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?


Solution

  • 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 and S 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:

    • a type compatible with the effective type of the object [but struct CS is not compatible with struct S]
    • a qualified version of a type compatible with the effective type of the object [but struct CS is not a version of structure S or of a compatible type],
    • the signed or unsigned type compatible with… [but struct CS is not a signed or unsigned type because those are integer types],
    • the signed or unsigned type compatible with a qualified version… [ditto],
    • an aggregate or union type that includes one of the aforementioned types among its members [but the sole member of CS, const int, is not compatible with any of the aforementioned],
    • a character type [but 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:

    • the signed or unsigned type compatible with a qualified version of the underlying type of the effective type of the object [and const int is compatible with const int, which is a qualified version of the effective type, int].

    Footnote

    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.