Declaration specifiers and declarators - c

With reference to the question Where in a declaration may a storage class specifier be placed? I started analyzing the concept of declaration-specifiers and declarators. Following is the accumulation of my understanding.
Declarations
Generally, the C declarations follow the syntax of declaration-specifiers declarators;
declaration-specifiers comprises of type-specifiers , storage-class-specifiers and type-qualifiers
declarators can be variables,pointers,functions and arrays etc..
Rules that I assume
declaration-specifiers can be specified in any order, as an example
There cannot be more than a single storage-class-specifier
On the other hand there can be multiple type-qualifiers
storage-class-specifier shall not go with the declarator
Questions
Q1: In the declaration of a constant pointer, I see a mix of declarator and type-qualifier as below
const int *const ptr; //Need justification for the mix of declarator and type-specifier
Q2: There can be a pointer to static int. Is there a possibility of providing the pointer a static storage class? Means the pointer being static.

I'm not sure I full understand you first question. In terms of C++03 grammar const is a cv-qualifier. cv-qualifier can be present in decl-specifier-seq (as a specific kind of type-specifier), which is a "common" part of the declaration, as well as in init-declarator-list, which is a comma-separated sequence of individual declarators.
The grammar is specifically formulated that a const specifier belonging to an individual pointer declarator must follow the *. A const specifier that precedes the first * is not considered a part of the individual declarator. This means that in this example
int const *a, *b;
const belongs to the left-hand side: decl-specifier-seq, the "common" part of the declaration. I.e. both a and b are declared as int const *. Meanwhile this
int *a, const *b;
is simply ill-formed and won't compile.
Your second question doesn't look clear to me either. It seems that you got it backwards. You claim that "there can be a pointer to static int"? No, there's no way to declare such thing as "pointer to static int". You can declare a static pointer to int though
static int *p;
In this case the pointer itself is static, as you wanted it to be.

Q2: There can be a pointer to static int. Is there a possibility of providing the pointer a static storage class? Means the pointer being static.
Well, yes:
static T *a;
Declare a a pointer to T. a has static storage duration.

Generally, the C "declarations" is like this declaration-specifiers declarators;
Here, "declaration-specifiers" comprises of type-specifiers , storage-class-specifiers and type-qualifiers .
"declarators" can be variables,pointers,functions and arrays etc..
errors like :- [Error] expected declaration specifiers or '...' before string constant
This type of errors come ,reason for it is problem in declarations.

Related

What does "parenthesized declarators" mean in C syntax

The C Standard specifies some translation limits. I cannot understand the following two:
12 pointer, array, and function declarators (in any combinations) modifying an arithmetic,
structure, union, or void type in a declaration
63 nesting levels of parenthesized declarators within a full declarator
Can someone explain and give simple examples for:
declarator modifying a declaration and
parenthesized declarators within a full declarator
C 2018 2.7.6 specifies declarators, including showing the grammatical definition of a declarator as:
declarator: pointeropt direct-declarator
direct-declarator:
        identifier
        ( declarator )
        direct-declarator [ … ]
        direct-declarator ( … )
pointer:
        * type-qualifier-listopt
(The syntax used for this grammar is briefly discussed in 6.1. Even more briefly, “direct-declarator:” means, when the compiler is looking for a direct-declarator, it will accept any of the options listed on the following lines. The subscript “opt” means an item is optional. I have consolidated multiple options in the original to “…” since the details are unimportant in this question. For example, array declarators may be empty or may contain an assignment expression and may have static, and so on.)
In int x;, x is a declarator. The declaration causes x to be declared as an int.
In int *x, *x is a declarator. It causes x to be declared as an int *. So it has modified the declaration.
In int (*x[3][4])(float, char);, x is a declarator (because it is a direct-declarator that is an identifier). It is inside an array declarator (a term shown in 6.7.6.2 to refer to the options with [ and ] above), x[3]. That is inside another array declarator, x[3][4]. That is a direct-declarator inside a pointer declarator (shown in 6.7.6.1 to refer to the pointer option above), *x[3][4]. That is inside a parenthesized declarator, (*x[3][4]) (a term that is not explicitly defined but is clear). And that is inside a function declarator (6.7.6.3), (*x[3][4])(float, char). Thus, this declaration has five nested levels of declarators modifying the int type in the declaration, four of which are pointer, array, or function declarators (one is a parenthesized declarator). It declares x to be an array of 3 arrays of 4 pointers to a function taking float and char and returning int.
At this point, the “63 nesting levels of parenthesized declarators within a full declarator” should be clear. int (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((x))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))); has 63 nested levels of parenthesized declarators within a full declarator. This does not normally arise in direct drafting of a declaration, but construction of declarations by use of various macros might give rise to multiple levels of parentheses.
The standard is treating pointer, array, and function declarators as something the compiler must keep track of, and is therefore subject to a modest limit of 12 nested levels, whereas parentheses are a superficial grammatical construction that need be tracked only during parsing and can then be discarded internally, and so are less burdensome on a compiler and have a more generous limit.

C declarator understanding

I am reading about declarators in C99 ISO Standard and I am struggling to understand the following passage:
5 If, in the declaration ”T D1”, D1 has the form
    identifier
then the type specified for ident is T.
6 If, in the declaration “T D1”, D1 has the form
    ( D )
then ident has the type specified by the declaration “T D”. Thus, a declarator in parentheses is identical to the unparenthesized declarator, but the binding of complicated declarators may be altered by parentheses.
You omitted an important previous paragraph:
4 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.
So, when we get to paragraphs 5 and 6, we know the declaration we are considering contains within it some identifier which we label ident. E.g., in int foo(void), ident is foo.
Paragraph 5 says that if the declaration “T D1” is just ”T ident”, it declares ident to be of type T.
Paragraph 6 says that if the declaration “T D1” is just ”T (ident)”, it also declares ident to be of type T.
These are just establishing the base cases for a recursive specification of declaration. Clause 6.7.5.1 goes on to say that if the declaration “T D1” is ”T * some-qualifiers D” and the same declaration without the * and the qualifiers, ”T D” would declare ident to be “some-derived-type T” (like “array of T” or “pointer to T”), then the declaration with the * and the qualifiers declares ident* to be “some-derived-type some-qualifiers pointer to T”.
For example, int x[3] declares x to be “array of 3 int”, so this rule in 6.7.5.1 tells us that “int * const x[3] declares x to be “array of 3 const pointer to int”—it takes the “array of 3” that must have been derived previously and appends “const pointer to” to it.
Similarly, clauses 6.7.5.2 and 6.7.5.3 tell us to append array and function types to declarators with brackets (for subscripts) and postfix parentheses.
In the quoted definitions, T is a type (e.g. int or double or struct foo or any typedef name), and D1 is a declarator.
Declarators may be arbitrarily complex, but are constructed from a small number of simple steps. They mention that it is defined inductively, which is another way of saying it has a recursive defintion.
The simplest declarator is just an identifier name, such as x or foo. So T could be int and D1 could be x.
It then goes on to say that a declarator may be parenthesized, e.g. (x) or ((x)) etc. These are degenerate cases, and are both equivalent to just x, but there are times when parentheses are needed to produce the desired grouping. For example, the declarators *x[10] and (*x)[10] mean quite different things. The former is equivalent to *(x[10]) and is an array of pointers, while the latter is a pointer to an array.
There is more to it (arrays, pointers, functions, etc.), but this covers the portion referenced in the question.
In long int *ident[4]; (array (4) of pointer to long int) long int is the specifier list, *ident[4] is the declarator.
You can put the declarator in parentheses without changing semantics:
long int (*ident[4]);, but in a more complex declaration such as
long int (*ident[4])[5]; (array (4) of pointer to array (5) of long int), the parentheses affect binding as without them, long int *ident[4][5]; would be interpreted as array (4) of array (5) of pointer to long int.
It works this way because the declarator part can recursively encompass another declarator and so on.
From the old C manual (it got a bit more indirect in later standardized Cs, but the basic principle is the same):
declarator:
identifier
* declarator
declarator ( )
declarator [ constant-expression opt ]
( declarator )
To put it in another way, C declarators are a way to prefix pointer-to/function-returning/array-of to some specifier list (e.g., long int) and these can be combined to create a chain.
Normally, in the creation of that chain, the suffixes (()=function returning, []=array of) bind tighter than the */pointer-to prefix. You can use parentheses to override this and force * (or several of them) to bind now without it getting overpowered by a []/() suffix.
E.g.:
long int *ident[4][5]; //array (4) of array (5) of pointer to long int
//the suffixes win over the `*` prefix
long int (*ident[4])[5]; //array (4) of pointer to array (5) of long int
//the parens force `pointer to` right after `array (4)`
//without letting the `[]` suffix overpower the pointer-to/`*` declarator prefix
P.S.:
C disallows arrays of functions and functions returning functions or arrays. These are both nicely expressible in the grammar but C's semantic check will want you to but a pointer-to link in there to prevent these.
cdecl.org may be very helpful if you want to play with these, and perhaps even more so because it doesn't do said semantic check, therefore allowing you even things like int foo[]()(); (array of function returning function returning int), which nicely demonstrate how the grammar works, even though a C compiler would reject them.
...binding of complicated declarators
This is a very nice hint in the specs.
The rule itself is really hard to analyze because of it's recursiveness. Also the relation and parts of declaration and declarator are relevant.
The results are:
() and [] are the innermost direct-declarator parts, declaring (directly by symbol) functions and array names to the left
* declares a name as pointer on the right
(...) is needed for...complicated cases, to change the default association.
The grouping parens lead you on your way from inside (identifier) to outside (type specifier on the left, say "int").
In the end it is all about the pointer symbol * and what it refers to. The reformulated (brackets mean optional, not array here!) syntax is:
declarator: [* [qual]] direct-declarator
direct-declarator: (declarator)
foo() is a DD (direct declarator)
*foo is a declarator. ("indirect" by deduction)
*foo() is *(foo()). foo stays a function, () and [] bind strongest. The * is the return type.
(*foo)() makes foo a pointer. One to a function.
BTW this also explains why in a list of declarator.
int const * a, b
both are const int, but only a is a pointer
The const belongs to int and the star only to a. This makes it more clear,
const int x, *pi
But this already is borderline obfuscation. Like modern poetry. Good for certain occasions.
Even without parens there is a slight U-turn in parsing. But this is natural:
3 2 0 1
int *foo()
This standard situation (and similar ones) had to be simple. Also the famous multidim arrays like int a[10][10][10].
3 1 0 2
int (*foo)()
Here the parens force "foo" to be what is on the left side (a pointer).
Complicated Declarations have their own chapter in K&R C book.
This is sort of the simplest complicated declaration:
int (*(*foo)[])()
It debugs to abstract type:
int (*(*)[])()
With (*) replaced:
int (*F[])()
The missing array size gives compiler warning - "assuming one element".
As abstract type:
int (*[])()
But:
int *G[]()
--> error: declaration of 'G' as array of functions
Yes, you can, even recursively, but with the * indirection and parens. This makes an onion of parens with the identifeir in the middle, stars on the left and [] and () on the right.
The C11 specs has this monster. The ... declare variadic args:
int (*fpfi(int (*)(long), int))(int, ...)
With all params removed:
int (*fpfi())()
Simply a function returning a pointer. One to a function returning int.
But the first param of fpfi is a function itself - a pointer to function with return type and its own params:
int (*)(long)
Non-abstractly:
int (*foo)(long)
A pointer to a function that "converts" a long to int, formally.
That is the param. Only. The return value is also a function pointer, and the pointed-to function's params and return type are outermost. Dropping the whole inner function thing (int (*)(long), int):
int (*pfi)(int, ...)
Or more generic/incomplete:
int (*pfi)()
"T (D)" Onion Rule
So this onion-game repeats itself. Inside-out and right-left-right-left between [], () and *. Syntax is not the problem, but semantics.

an error about C struct array in formal parameter

I have got the following code:
struct student_info;
void paiming1(struct student_info student[]);
struct student_info
{
int num;
char name[6];
};
The IDE gives an error
error: array type has incomplete element type ‘struct student_info’
void paiming1(struct student_info student[]);
But if I use void paiming1(struct student_info *student); it works OK. Why is that? I am using GCC.
С language unconditionally requires array element type in all array declarations to be complete. Period.
6.7.6.2 Array declarators
Constraints
1 [...] The element type shall not be an incomplete or function
type. [...]
No exception is made for array declarations used in function parameter lists. This is different from C++ - the latter drops this completeness requirement for function parameter lists
struct S;
void foo(struct S[]); // OK in C++, invalid in C
Considering that in parameter list declaration type T [] is later adjusted to type T * anyway, this requirement might appear to be excessive. (And this is why C++ relaxed it.) Nevertheless, this restriction is present in C language. This is just one of the quirks of C language.
As you already know, you can explicitly switch to the equivalent
void paiming1(struct student_info *student);
form to work around the issue.
Careful reading of the standard makes it clear that in C99 and C11 the declaration is supposed to be a constraint violation. C11 6.7.2.6 Array declarations p1
In addition to optional type qualifiers and the keyword static, the [ and ] may delimit an expression or *. If they delimit an expression (which specifies the size of an array), the expression shall have an integer type. If the expression is a constant expression, it shall have a value greater than zero. The element type shall not be an incomplete or function type. The optional type qualifiers and the keyword static shall appear only in a declaration of a function parameter with an array type, and then only in the outermost array type derivation.
Since this contains references to the * that is valid only in function declarations that are not definitions, and nowhere else, the constraints as whole needs to be taken as applying to parameters.
For C90 the case is more complicated. This is actually discussed in C90 Defect Report 47 from 10 December, 1992.
2 of the 6 declarations given there are
/* 1. */ struct S;
/* 3. */ struct S *g(struct S a[]) { return a; }
and the defect report asks if these are strictly-conforming. It must be noted that unlike the question, these are prototypes that are part of definition rather than just declaration.
Nevertheless, the standard committee responds by saying that
The struct S is an incomplete type (subclause 6.5.2.3, page 62, lines 25-28). Also, an array of unknown size is an incomplete type (subclause 6.5.4.2, page 67, lines 9-10). Therefore, arrays of either of the above are not strictly conforming (subclause 6.1.2.5, page 23, lines 23-24). This makes declarations 3, 4, and 6 not strictly conforming. (But an implementation could get it right.)
As an aside, array parameters are adjusted to pointer type (subclause 6.7.1, page 82, lines 23-24). However, there is nothing to suggest that a not-strictly-conforming array type can magically be transformed into a strictly conforming pointer parameter via this rule.
The types in question can be interpreted two different ways. (Array to pointer conversion can happen as soon as possible or as late as possible.) Hence a program that uses such a form has undefined behavior.
(emphasis mine)
Since this has not been clarified in writing since 1992, we must agree that the behaviour is undefined and therefore the C standard imposes no requirements, and a compiler that successfully compiles this this can still conform to C90.
The committee also states that there is no constraint violation in C90, therefore a C90 conforming compiler need not output any diagnostics.
I've edited the answer; I previously claimed that this would apply to C99 and C11 alike, but the text was changed in C99 as above, therefore this is a constraint violation in C99, C11.
Compiler doesn't know anything about size of struct student_info until it is declared. This should work:
struct student_info
{
int num; //学号
char name[6]; //姓名
char sex[5]; //性别
char adress[20]; //家庭住址
char tel[11]; //电话
int chinese,math,english,huping,pingde,jiaoping,paiming1,paiming2;
double ave,zhongping;
};
void paiming1(struct student_info student[]);
void paiming2(struct student_info student[]);
When you declare it as a pointer using *, compiler knows the size of the argument (its an address).

Declarators semantics in C99

According to the ISO/IEC 9899:1999 6.7.5 §2,
Each declarator declares one identifier, and asserts that when an operand of the same
form as the declarator appears in an expression, it designates a function or object with the
scope, storage duration, and type indicated by the declaration specifiers.
I have no idea why an expression suddenly shows up in the declarators semantics. Would you give me some examples that can help me understand the meaning?
Say you have the declaration
int foo[42];
The declarator part is foo[42]. Whenever something of same form (ie foo followed by [ followed by an expression followed by ]) appears within an expression (and the delaration is in scope), the type of that sub-expression will be of the declared type int.
To put it another way: As far as syntax goes, a declaration like
int *bar;
does not declare bar to be of type int *, but instead declares *foo to be of type int.
For a more involved example, take the declaration
float (*op[42])(float, float);
In an expression, an operand of same form could look like this
c = (*op[i])(a, b);
According to the quotation, the right-hand side would have type float.
This implies that
*op[i]
must have function type (we ignore the fact that function designators decay to corresponding pointer types and function invocation via postfix () actually works on pointers, not designators).
This in turn implies that
op[i]
must denote a function pointer and we finally arrive at
op
denoting an array of function pointers as that's what we can apply postfix [] on and get back the correct type.
Fun, isn't it ;)
Say you declare:
static int const i, j, k;
That is same as:
static int const i;
static int const j;
static int const k;
The declarator specifer, static int const is applied to all the identifiers.
You can also extend that logic to functions and function pointers.
static int i, (*fun1)(void), fun2(void);
which is same as:
static int i;
static int (*fun1)(void);
static int fun2(void);
As far as the appears in an expression part goes, there is some commentary at http://c0x.coding-guidelines.com/6.7.5.pdf. It says:
Semantics
Each declarator declares one identifier, and asserts that when an operand of the same form as the declarator appears in an expression, it designates a function or object with the scope, storage duration, and type indicated by the declaration specifiers.
Commentary
The form of an identifier in an expression is likely to be the same as that in the declarator. For instance, the declarator * x will have this form in an expression when the value pointed to by x is required and the declarator y[2] will have this form in an expression when an element of the array y is referred to. It is the declarator portion of a declaration that declares the identifier. There is a special kind of declarator, an abstract-declarator, which does not declare an identifier
I interpret the above to mean:
If you declare:
int *x;
and use *x in an expression, the type of *x is int.
If you declare
static int const *x;
and use *x in an expression, the type of *x is static int const.
Additional references
Static Variable Declaration (C)
Is [ ] also a declarator (when used in parameter declaration) in C?
C -- Accessing a non-const through const declaration
All that's saying is, given a declaration like
const int a = 5; // declaration
then the operand a in an expression like
x = a + b
within the same scope has type const int.

Which part of the C standard allows this code to compile?

I was bug-fixing some code and the compiler warned (legitimately) that the function dynscat() was not declared — someone else's idea of an acceptable coding standard — so I tracked down where the function is defined (easy enough) and which header declared it (none; Grrr!). But I was expecting to find the details of the structure definition were necessary for the extern declaration of qqparse_val:
extern struct t_dynstr qqparse_val;
extern void dynscat(struct t_dynstr *s, char *p);
extern void qqcat(char *s);
void qqcat(char *s)
{
dynscat(&qqparse_val, s);
if (*s == ',')
dynscat(&qqparse_val, "$");
}
The qqcat() function in the original code was static; the extern declaration quells the compiler warning for this snippet of the code. The dynscat() function declaration was missing altogether; again, adding it quells a warning.
With the code fragment shown, it's clear that only the address of the variable is used, so it makes sense at one level that it does not matter that the details of the structure are not known. Were the variable extern struct t_dynstr *p_parseval;, you'd not be seeing this question; that would be 100% expected. If the code needed to access the internals of the structure, then the structure definition would be needed. But I'd always expected that if you declared that the variable was a structure (rather than a pointer to the structure), the compiler would want to know the size of the structure — but apparently not.
I've tried provoking GCC into complaining, but it doesn't, even GCC 4.7.1:
gcc-4.7.1 -c -Wall -Wextra -std=c89 -pedantic surprise.c
The code has been compiling on AIX, HP-UX, Solaris, Linux for a decade, so it isn't GCC-specific that it is accepted.
Question
Is this allowed by the C standard (primarily C99 or C11, but C89 will do too)? Which section? Or have I just hit on an odd-ball case that works on all the machines it's ported to but isn't formally sanctioned by the standard?
What you have is an incomplete type (ISO/IEC 9899:1999 and 2011 — all these references are the same in both — §6.2.5 ¶22):
A structure or union type of unknown content (as described in §6.7.2.3) is an incomplete
type.
An incomplete type can still be an lvalue:
§6.3.2.1 ¶1 (Lvalues, arrays, and function designators)
An lvalue is an expression with an object type or an incomplete type other than void; ...
So as a result it's just like any other unary & with an lvalue.
Looks like a case of taking the address of an object with incomplete type.
Using pointers to incomplete types is totally sane and you do it each time you use a pointer-to-void (but nobody ever told you :-)
Another case is if you declare something like
extern char a[];
It is not surprising that you can assign to elements of a, right? Still, it is an incomplete type and compilers will tell you as soon as you make such an identifier the operand of a sizeof.
your line
extern struct t_dynstr qqparse_val;
Is an external declaration of an object, and not a definition. As an external object it "has linkage" namely external linkage.
The standard says:
If an identifier for an object is declared with no linkage, the type for the object shall be complete by the end of its declarator, ...
this implies that if it has linkage the type may be incomplete. So there is no problem in doing &qqparse_val afterwards. What you wouldn't be able to do would be sizeof(qqparse_val) since the object type is incomplete.
A declaration is necessary to "refer" to something.
A definition is necessary to "use" something.
A declaration may provide some limited definition as in "int a[];"
What stumps me is:
int f(struct _s {int a; int b;} *sp)
{
sp->a = 1;
}
gcc warn the 'struct _s' is declared inside parameter list. And states "its scope is ONLY this definition or declaration,...". However it does not give an error on "sp->a" which isn't in the parameter list. When writing a 'C' parser I had to decide where the definition scope ended.
Focusing on the first line:
extern struct t_dynstr qqparse_val;
It can be divided into the separate steps of creating the type and the variable, resulting in this equivalent pair of lines:
struct t_dynstr; /* declaration of an incomplete (opaque) struct type */
extern struct t_dynstr qqparse_val; /* declaration of an object of that type */
The second line looks just like the original, but now it's referring to the type that already exists because of the first line.
The first line works because that's just how opaque structs are done.
The second line works because you don't need a complete type to do an extern declaration.
The combination (second line works without the first line) works because combining a type declaration with a variable declaration works in general. All of these are using the same principle:
struct { int x,y; } loc; /* define a nameless type and a variable of that type */
struct point { int x,y; } location; /* same but the type has a name */
union u { int i; float f; } u1, u2; /* one type named "union u", two variables */
It looks a little funny with the extern being followed immediately by a type declaration, like maybe you're trying to make the type itself "extern" which is nonsense. But that's not what it means. The extern applies to the qqparse_val in spite of their geographical separation.
Here's my thoughts relative to the standard (C11).
Section 6.5.3.2: Address and indirection operators
Constraints
Paragraph 1: The operand of the unary & operator shall be either a function designator, the result of a [] or unary * operator, or an lvalue that designates an object that is not a bit-field and is not declared with the register storage-class specifier.
Paragraph 2: The operand of the unary * operator shall have pointer type.
Here, we don't specify any requirement on the object, other than that it is an object (and not a bitfield or register).
On the other hand, let's look at sizeof.
6.5.3.4 The sizeof and _Alignof operators
Constraints
Paragraph 1: The sizeof operator shall not be applied to an expression that has function type or an incomplete type, to the parenthesized name of such a type, or to an expression that designates a bit-field member. The _Alignof operator shall not be applied to a function type or an incomplete type.
Here, the standard explicitly requires the object to not be an incomplete type.
Therefore, I think this is a case of what is not explicitly denied is allowed.

Resources