How does the resolution of a parameter list work when a typedef and additional qualifier are used? Does it work like a textual replacement, similar to a #define?
Example:
typedef struct Parameters_t * Parameters_pot;
f(const Parameters_pot pars)
f(Parameters_pot const pars)
Does the 'const' qualifier in the parameter list in both function delarations relate to the data type, meaning the pointer is pointing to a constant structure? Or does the position of the 'const' matter and in the first function declaration the 'const' means that the structure object is constant and in the second declaration it means that the pointer is constant?
It is not text replacement. And that's exactly why hiding pointers or arrays behind a typedef
is considered very bad practice. Both examples here are equivalent to:
f(struct Parameters_t * const pars)
Only since it was requested, here's an attempt at explaining the formal language lawyering:
C17 6.7.8 states that a typedef has the form as typedef T type_ident;
where type_ident
is a type name "with the type specified by the declaration
specifiers in T
". Specifiers plural. To understand this we have to step back to 6.7 and 6.7.6 regarding "declarators" and "declaration-specifiers".
6.7.6 says that every declaration can be said to consist of T D1
where T
is the declaration specifiers and D1
the declarator.
A "declaration-specifier" is everything you may write in front of the identifier in a declaration, including storage class specifiers (static
etc) and type qualifiers (const
etc) and of course also type specifiers (int
etc). Several of these may optionally exist in the same declaration.
Except for storage class specifiers, which as a special case can never be combined with each other (6.7.1). And as it happens, typedef
counts as a storage class specifier, so we can't have things like static
inside a typedef
.
A "declarator" contains the identifier as well as array notation etc. It may optionally contain a *
for pointers. The important part here is that *
belongs to the declarator.
The formal syntax (6.7.6) goes as:
declarator:
pointeropt direct-declarator
...
pointer:
*
type-qualifier-listopt
Going back to typedef T type_ident;
, then everything that is T
belongs to the type, including any qualifiers, pointer syntax, array syntax etc that was used.
Thus should we write something like const T
, then const
is applied to the whole type T
. Now if we had 'T' as for example const int*
"a pointer to const int" then const T
will therefore create a const int* const
, "a const pointer to const int".
If T
was a function, then it will attempt to declare a "const function" which is invalid. And so on.
So for your example typedef struct Parameters_t * Parameters_pot;
, the const
is applied to the type struct Parameters_t *
.
Therefore f(const Parameters_pot pars)
is equivalent to f(struct Parameters_t * const pars)
.
f(Parameters_pot const pars)
also has the same equivalent meaning, for the same reason as int const
and const int
are equivalent - the order in which declaration specifiers may appear isn't specified most of the time.