clanguage-lawyerstandardsc11

C standard - const qualification of struct members


I encountered a problem at work today regarding const propagation in structs. I was trying to add some const correctness to old code when I ran into an issue where two different compilers disagreed, GCC and IAR. I think it boils down to whether adding const to a struct variable makes pointer members being <type>* const or const <type>* const.

Minimal example:

struct S
{
    char* p;
};

void f(const struct S* s)
{
    char* non_const_char = s->p;
    *non_const_char = 'a'; // Modyfing data that could be argued being part of const struct
}

GCC allows this code, but IAR fails saying that the assignment non_const_char can't be made, due to

value of type "char const *" cannot be assigned to an entity of type "char *"

According to multiple SO questions GCC is correct, for example Scope of 'const' for pointers to protect underlying members of struct in C . This is also corroborated by cppreference.

However, I'm not standardese-fluent enough to parse which part of the standard actually says that const-qualification propagates to the struct members, but not to the types pointed to by member-pointers. Can anyone show me/quote what part states this? And has this ever changed with different standard versions?


Solution

  • However, I'm not standardese-fluent enough to parse which part of the standard actually says that const-qualification propagates to the struct members, but not to the types pointed to by member-pointers. Can anyone show me/quote what part states this?

    C 2018 6.5.2.3 3 and 4 say (emphasis added):

    3 A postfix expression followed by the . operator and an identifier designates a member of a structure or union object. The value is that of the named member, and is an lvalue if the first expression is an lvalue. If the first expression has qualified type, the result has the so-qualified version of the type of the designated member.

    4 A postfix expression followed by the -> operator and an identifier designates a member of a structure or union object. The value is that of the named member of the object to which the first expression points, and is an lvalue. If the first expression is a pointer to a qualified type, the result has the so-qualified version of the type of the designated member.

    Thus, if s has type “pointer to const structure,” then s->p is const qualified.

    And has this ever changed with different standard versions?

    The text bolded above appears identically in C 1990 6.3.2.3, which is the first version of the ANSI C standard.

    … but not to the types pointed to by member-pointers.

    There is no citation for this because the fact that *s->p is not const-qualified (although s->p is) is due to the fact that nothing in the C standard says it is const-qualified or that the type of what p points to would be changed because its containing structure is const-qualified.

    C 2018 6.5.3.2 4 tells us that applying * to a pointer yields an lvalue of the pointed-to-type:

    … If the operand has type "pointer to type", the result has type “type”…

    And nothing there says that the const from the pointer or its containing structure will be applied to the pointed-to type.