Reputation: 222362
C 2018 6.7.6.1 1 says:
If, in the declaration “T D1”, D1 has the form
* type-qualifier-listopt D
and the type specified for ident in the declaration “T D” is “derived-declarator-type-list T”, then the type specified for ident is “derived-declarator-type-list type-qualifier-list pointer to T”. For each type qualifier in the list, ident is a so-qualified pointer.
This question is about the final sentence, but let’s work through the first one first.
Consider the declaration int * const * foo
. Here T is int
, D1 is * const * foo
, type-qualifier-list is const
, and D is * foo
.
Then T D is int * foo
, and that specifies “pointer to int
” for the ident foo
, so derived-declarator-type-list is “pointer to”. (There is no overt explanation of derived-declarator-type-list in the standard, but 6.7.8 3, discussing typedef
, says it “is specified by the declarators of D.”)
Substituting these into the final clause of the first sentence tells us that T D1 specifies the type for foo
is “pointer to const
pointer to int
”. Fine so far.
But then the final sentence tells us that for each type qualifier in the list (which is const
), ident is a so-qualified pointer. So it says foo
is a const
pointer.
But it is not; we commonly interpret int * const * foo
to declare foo
to be a non-const
pointer to a const
pointer to int
.
Is this a mistake in the standard or is there another interpretation for the final sentence?
Upvotes: 12
Views: 509
Reputation: 222362
For the record, there are three answers that were deleted by their authors, supporting a conclusion that this aspect of the C standard is tricky. I do not believe the other current answer correctly matches the example source text (int * const * foo
) to the symbols in the passage from the standard (T, D1, type-qualifier-list, and so on).
Thus I conclude this is indeed a mistake in the standard.
I believe a fix is simply to remove the last sentence, “For each type qualifier in the list, ident is a so-qualified pointer.” Any qualifier in the type-qualifier list is already incorporated, correctly, into the previous sentence, and any qualifier inside D is already incorporated in that declarator. So this seems like just a superfluous sentence that may have arisen inadvertently in some edit.
Upvotes: 0
Reputation:
With T as int
and D as * foo
, the "T D" does not give ident the type T, but pointer to T.
The second part of the if
:
If ... and the type specified for ident in the declaration “T D” is “derived-declarator-type-list T”
seems to make sure that the nesting is correct.
With two stars, you would have to use D2 - D1 - D before you reach the direct declarator.
The second example:
typedef int *int_ptr;
const int_ptr foo;
shows how tricky it can get; const
is far away from ident, but still the pointer is constant, not the data.
The specs call this a clarification - now that is a mistake.
Interestingly in the next section "6.7.6.2 Array declarators" there is a footnote after the corresponding semantical definition:
When several "array of" specifications are adjacent, a multidimensional array is declared.
The case of several adjacent "pointer of" specifications is not mentioned. Kind of a "mistake".
In 1988 the "Reference Manual" had the D1 and D switched (!), but otherwise the wording is almost identical. (there is type-modifier instead of derived-declarator-type-list).
The examples are similar: the main example is int *ap[]
:
Here ap[] plays the role of D1; a declaration "int ap[]" would ...
With int **pp
, *pp
plays that role.
Upvotes: 2