arrayscpointerslanguage-lawyerdeclaration

How do we call this "int * name[4]" using this phrase “derived-declarator-type-list array of T"


In C17 we have this(6.7.6.2 #3):

If, in the declaration “T D1”, D1 has one of the forms:

D [ type-qualifier-listopt assignment-expressionopt ]
D [ type-qualifier-listopt assignment-expression ]
D [ type-qualifier-list static assignment-expression ]
D [ type-qualifier-listopt ]

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 array of T”.144) (See 6.7.6.3 for the meaning of the optional type qualifiers and the keyword static.)

Derived it is not float, or int, or char, it is "array of", or "pointer to".

For example, we have int name [2][3], here T is int, and D1 is A[2][3], with the form D[3] where D is A[2], so T D is int A[2], or "array of 2 int". Then the declared type of A is "array of 2 array of 3 int".

But what about int * name[4]?

Here, the "derived-declarator-type-list T" will be int * name. And, it turns out, the "derived-declarator-type-list array of T" will be a pointer to an array of 4 elements?

In the end, this is wrong, because int *name[4] is an array of 4 pointers. Please explain it to me.


Solution

  • Parsing int * name [ 4 ] ; as a declaration goes via this production:

    declaration-specifiers init-declarator-list ;
    

    If we build up from the bottom, we find that the declaration-specifiers consists only of one type-specifier, int, and the init-declarator-list consists of exactly one declarator, * name [ 4 ]. These are T and D1, respectively, for the purposes of paragraph C17 6.7.6.2/3:

    If, in the declaration "T D1", D1 has one of the forms:

    D [ type-qualifier-listopt assignment-expressionopt ]

    [...]

    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 array of T".

    That is the relevant provision. D is the part of D1 preceding the [, so just * name in this case, and therefore T D is int * name.

    The spec requires us to analyze T D alone, as if it were a whole declaration, to determine the type it would declare for the identifier name. In this case, that is int * (section 6.7.6.1), which therefore corresponds to the derived-declarator-type-list T relevant to the analysis. At this point we need to recognize that the spec does not define the symbol derived-declarator-type-list, nor either derived-declarator-type or even derived-declarator, except contextually by this very provision and others similar to it in the specs for pointer, function, and typedef declarations. We are therefore left to reconcile this with paragraphs 6.7.6/4-5:

    In the following subclauses, consider a declaration

         T D1
    

    where T contains the declaration specifiers that specify a type T (such as int) and D1 is a declarator that contains an identifier ident. The type specified for the identifier ident in the various forms of declarator is described inductively using this notation.

    If, in the declaration “T D1”, D1 has the form

        identifier
    

    then the type specified for ident is T.

    Note well that the spec therein uses typeface alone to distinguish two separate "T" symbols. One, whose typeface I have translated as T, represents all grammatic components of a declaration that precede the the declarator, whereas the other, styled with italic typeface, represents the abstract data type conveyed by the C semantics of T.

    Now observe also that for T D1 to represent grammatic form of a complete declaration, T must be able to absorb declaration specifiers that do not contribute to the declared data type, T. Such specifiers include storage class specifiers such as static and function specifiers such as inline, among others. And that is the connection we need to make with derived-declarator-type-list. This symbol represents all those specifiers in T that do not contribute to specifying the data type T.

    Now, finally, we can recognize that when we interpret our int * in terms of a derived-declarator-type-list T, the derived-declarator-type-list is empty and the T is "pointer to int". Thus, going back to paragrap[h 6.7.6.2/3, the overall declaration int * name [ 4 ] ; declares name "as array of pointer to int".

    Overall, this is a formalization of the same procedure you have probably learned for interpreting C declarations. We recognize, by grammatic form, that the we need to build up the full declarator from