Namespaces
Variants
Views
Actions

Difference between revisions of "c/language/declarations"

From cppreference.com
< c‎ | language
m (References)
 
(16 intermediate revisions by 9 users not shown)
Line 2: Line 2:
 
{{c/language/declarations/navbar}}
 
{{c/language/declarations/navbar}}
  
A {{rlp|declarations|declaration}} is a C language construct that introduces one or more  {{rlp|identifier}}s into the program and specifies their meaning and properties.
+
A ''declaration'' is a C language construct that introduces one or more  {{rlp|identifier}}s into the program and specifies their meaning and properties.
  
Declarations may appear in any scope. Each declaration ends with a semicolon (just like {{rlp|statements|a statement}}) and consists of two distinct parts:
+
Declarations may appear in any scope. Each declaration ends with a semicolon (just like {{rlp|statements|a statement}}) and consists of {{rev inl|until=c23|two}}{{rev inl|since=c23|three}} distinct parts:
 
{{sdsc begin}}
 
{{sdsc begin}}
{{sdsc| {{spar|specifiers-and-qualifiers}} {{spar|declarators-and-initializers}} {{ttb|;}} }}
+
{{sdsc|num=1| {{spar|specifiers-and-qualifiers}} {{spar|declarators-and-initializers}}{{mark optional}} {{ttb|;}} }}
 +
{{sdsc|num=2|notes={{mark since c23}}| {{spar|attr-spec-seq}} {{spar|specifiers-and-qualifiers}} {{spar|declarators-and-initializers}} {{ttb|;}} }}
 +
{{sdsc|num=3|notes={{mark since c23}}| {{spar|attr-spec-seq}} {{ttb|;}} }}
 
{{sdsc end}}
 
{{sdsc end}}
 
where
 
where
 
{{par begin}}
 
{{par begin}}
 
{{par|{{spar|specifiers-and-qualifiers}} | whitespace-separated list of, in any order,
 
{{par|{{spar|specifiers-and-qualifiers}} | whitespace-separated list of, in any order,
* exactly one type specifier:
+
* type specifiers:
 
:* {{c|void}}
 
:* {{c|void}}
 
:* the name of an {{rlp|arithmetic_types|arithmetic type}}
 
:* the name of an {{rlp|arithmetic_types|arithmetic type}}
 
:* the name of an {{rlp|atomic|atomic type}}
 
:* the name of an {{rlp|atomic|atomic type}}
 
:* a name earlier introduced by a {{rlp|typedef}} declaration
 
:* a name earlier introduced by a {{rlp|typedef}} declaration
:* {{rlp|struct}}, {{rlp|union}}, or {{rlp|enum}} specifier
+
:* {{rlpt|struct}}, {{rlpt|union}}, or {{rlpt|enum}} specifier
* zero or one storage-class specifiers: {{rlp|typedef}}, {{rlp|storage duration|auto, register, static, extern, thread_local}}
+
:* a {{rlp|typeof}} specifier {{mark since c23}}
* zero or more type qualifiers: {{rlp|const}}, {{rlp|volatile}}, {{rlp|restrict}}, {{rlp|atomic|_Atomic}}
+
* zero or one storage-class specifiers: {{rlpt|typedef}}, {{rlpt|constexpr}}, {{rlpt|auto}}, {{rlp|storage duration|{{tt|register}}, {{tt|static}}, {{tt|extern}}, {{tt|_Thread_local}}}}
* (only when declaring functions), zero or more function qualifiers: {{rlp|inline}}, {{rlp|_Noreturn|noreturn}}
+
* zero or more type qualifiers: {{rlpt|const}}, {{rlpt|volatile}}, {{rlpt|restrict}}, {{rlpt|atomic|_Atomic}}
* zero or more alignment specifiers: {{rlp|alignas}}
+
* (only when declaring functions), zero or more function specifiers: {{rlpt|inline}}, {{rlpt|_Noreturn}}
}}
+
* zero or more alignment specifiers: {{rlpt|_Alignas}}}}
{{par|{{spar|declarators-and-initializers}} | comma-separated list of {{spar|declarators}} (each declarator provides additional type information and/or the identifier to declare). Declarators may be accompanied by {{rlp|initialization|initializers}}. The {{rlp|enum}}, {{rlp|struct}}, and {{rlp|union}} declarations may omit {{spar|declarators}}, in which case they only introduce the enumeration constants and/or tags.
+
{{par|{{spar|declarators-and-initializers}} | comma-separated list of {{spar|declarators}} (each declarator provides additional type information and/or the identifier to declare). Declarators may be accompanied by {{rlp|initialization|initializers}}. The {{rlp|enum}}, {{rlp|struct}}, and {{rlp|union}} declarations may omit {{spar|declarators}}, in which case they only introduce the enumeration constants and/or tags.}}
}}
+
{{par|{{spar|attr-spec-seq}} | {{mark c23}}optional list of {{rlp|attributes}}, applied to the declared entities, or forms an attribute declaration if appears alone.}}
 
{{par end}}
 
{{par end}}
 +
 +
@1-2@ Simple declaration. Introduces one or more identifiers which denotes objects, functions, struct/union/enum tags, typedefs, or enumeration constants.
 +
@3@ Attribute declaration. Does not declares any identifier, and has implementation-defined meaning if the meaning is not specified by the standard.
  
 
For example,
 
For example,
Line 32: Line 37:
 
                 // "*b" is a declarator and NULL is its initializer
 
                 // "*b" is a declarator and NULL is its initializer
 
const int *f(void); // "int" is the type specifier
 
const int *f(void); // "int" is the type specifier
                     // "const" is type type qualifier
+
                     // "const" is the type qualifier
 
                     // "*f(void)" is the declarator
 
                     // "*f(void)" is the declarator
 
enum COLOR {RED, GREEN, BLUE} c; // "enum COLOR {RED, GREEN, BLUE}" is the type specifier
 
enum COLOR {RED, GREEN, BLUE} c; // "enum COLOR {RED, GREEN, BLUE}" is the type specifier
Line 38: Line 43:
 
}}
 
}}
  
The type of each identifier introduced in a declaration is determined by a combination of the type specified by the {{spar|type specifier}} and the type modifications applied by its {{spar|declarator}}.
+
The type of each identifier introduced in a declaration is determined by a combination of the type specified by the {{spar|type specifier}} and the type modifications applied by its {{spar|declarator}}. {{rev inl|since=c23|The type of a variable might also be inferred if {{tt|auto}} specifier is used.}}
 +
 
 +
{{rlp|attributes|Attributes}}{{mark since c23}} may appear in {{spar|specifiers-and-qualifiers}}, in which case they apply to the type determined by the preceding specifiers.
  
 
===Declarators===
 
===Declarators===
 
Each declarator is one of the following:
 
Each declarator is one of the following:
 
{{sdsc begin}}
 
{{sdsc begin}}
{{sdsc|num=1| {{spar|identifier}} }}
+
{{sdsc|num=1| {{spar|identifier}} {{spar|attr-spec-seq}}{{mark optional}} }}
 
{{sdsc|num=2| {{ttb|(}} {{spar|declarator}} {{ttb|)}} }}
 
{{sdsc|num=2| {{ttb|(}} {{spar|declarator}} {{ttb|)}} }}
{{sdsc|num=3| {{ttb|*}} {{spar|qualifiers}}{{mark optional}} {{spar|declarator}} }}
+
{{sdsc|num=3| {{ttb|*}} {{spar|attr-spec-seq}}{{mark optional}} {{spar|qualifiers}}{{mark optional}} {{spar|declarator}} }}
{{sdsc|num=4| {{spar|noptr-declarator}} {{ttb|[}} {{spar|static}}{{mark optional}} {{spar|qualifiers}}{{mark optional}} {{spar|expression}} {{ttb|]}}  
+
{{sdsc|num=4| {{spar|noptr-declarator}} {{ttb|[}} {{ttb|static}}{{mark optional}} {{spar|qualifiers}}{{mark optional}} {{spar|expression}} {{ttb|]}}  
 
{{spar|noptr-declarator}} {{ttb|[}} {{spar|qualifiers}}{{mark optional}} {{ttb|*}} {{ttb|]}}  
 
{{spar|noptr-declarator}} {{ttb|[}} {{spar|qualifiers}}{{mark optional}} {{ttb|*}} {{ttb|]}}  
 
}}
 
}}
Line 103: Line 110:
  
 
The end of every declarator that is not part of another declarator is a {{rlp|eval order|sequence point}}.
 
The end of every declarator that is not part of another declarator is a {{rlp|eval order|sequence point}}.
 +
 +
In all cases, {{spar|attr-spec-seq}} is an optional sequence of {{rlp|attributes}}{{mark since c23}}. When appearing immediately after the identifier, it applies to the object or function being declared.
  
 
===Definitions===
 
===Definitions===
Line 121: Line 130:
 
}}
 
}}
  
For {{rlp|stuct}}s and {{rlp|union}}s, declarations that specify the list of members are definitions:
+
For {{rlp|struct}}s and {{rlp|union}}s, declarations that specify the list of members are definitions:
 
{{source|1=
 
{{source|1=
 
struct X; // declaration
 
struct X; // declaration
Line 154: Line 163:
  
 
===Notes===
 
===Notes===
{{rev begin}}
+
{{rrev|until=c99|
{{rev|until=c99|
+
 
In C89, declarations within any {{rlp|statements#Compound_statements|compound statement}} (block scope) must appear in the beginning of the block, before any {{rlp|statements}}.
 
In C89, declarations within any {{rlp|statements#Compound_statements|compound statement}} (block scope) must appear in the beginning of the block, before any {{rlp|statements}}.
  
 
Also, in C89, functions returning {{c|int}} may be implicitly declared by the {{rlp|operator other#Function_call|function call operator}} and function parameters of type {{c|int}} do not have to be declared when using old-style {{rlp|function definition}}s.
 
Also, in C89, functions returning {{c|int}} may be implicitly declared by the {{rlp|operator other#Function_call|function call operator}} and function parameters of type {{c|int}} do not have to be declared when using old-style {{rlp|function definition}}s.
 
}}
 
}}
{{rev end}}
 
  
Empty declarators are prohibited; a declaration must {{rev inl|since=c11|be a {{rlp|_Static_assert|static_assert}} declaration or }} have at least one declarator or declare at least one struct/union/enum tag, or introduce at least one enumeration constant.
+
Empty declarators are prohibited; a simple declaration must have at least one declarator or declare at least one struct/union/enum tag, or introduce at least one enumeration constant.
  
{{rev begin}}
+
{{rrev|since=c99|
{{rev|since=c99|
+
If any part of a declarator is a {{rlp|array|variable-length array}} (VLA) declarator, the entire declarator's type is known as "variably-modified type". Types defined from variably-modified types are also variably modified (VM).
If any part of a declarator is a {{rlp|array|VLA array}} declarator, the entire declarator's type is known as "variably-modified type". Types defined from variably-modified types are also variably modified (VM).
+
  
Declarations of any variably-modified types may appear only at {{rlp|scope|block scope}} or function prototype scope and cannot be members of structs or unions. Although {{rlp|array|VLA}} can only have automatic {{rlp|storage duration}}, a VM type such as a pointer to a VLA may be static. There are other restrictions on the use of VM types, see {{rlp|goto}}, {{rlp|switch}}. {{lc|longjmp}}
+
Declarations of any variably-modified types may appear only at {{rlp|scope|block scope}} or function prototype scope and cannot be members of structs or unions. Although VLA can only have automatic or allocated {{rlp|storage duration}}, a VM type such as a pointer to a VLA may be static. There are other restrictions on the use of VM types, see {{rlp|goto}}, {{rlp|switch}}. {{lc|longjmp}}
 
}}
 
}}
{{rev end}}
 
  
{{rev begin}}
+
{{rrev|since=c11|
{{rev|since=c11|
+
 
{{rlp|_Static_assert|static_assert}}s are considered to be declarations from the point of view of the C grammar (so that they may appear anywhere a declaration may appear), but they do not introduce any identifiers and do not follow the declaration syntax.
 
{{rlp|_Static_assert|static_assert}}s are considered to be declarations from the point of view of the C grammar (so that they may appear anywhere a declaration may appear), but they do not introduce any identifiers and do not follow the declaration syntax.
 
}}
 
}}
{{rev end}}
+
{{rrev|since=c23|
 +
{{rlp|attributes|Attribute}} declarations are also considered to be declarations (so that they may appear anywhere a declaration may appear), but they do not introduce any identifiers. A single {{ttb|;}} without {{spar|attr-spec-seq}} is not an attribute declaration, but a statement.
 +
}}
  
 
===References===
 
===References===
 +
{{ref std c17}}
 +
{{ref std | section=6.7 | title=Declarations | p=78-105}}
 +
{{ref std end}}
 
{{ref std c11}}
 
{{ref std c11}}
 
{{ref std | section=6.7 | title=Declarations | p=108-145}}
 
{{ref std | section=6.7 | title=Declarations | p=108-145}}
 +
{{ref std end}}
 
{{ref std c99}}
 
{{ref std c99}}
 
{{ref std | section=6.7 | title=Declarations | p=97-130}}
 
{{ref std | section=6.7 | title=Declarations | p=97-130}}
 +
{{ref std end}}
 
{{ref std c89}}
 
{{ref std c89}}
{{ref std | section= | title=}}
+
{{ref std | section=3.5 | title=Declarations}}
 
{{ref std end}}
 
{{ref std end}}
  
 
===See also===
 
===See also===
 
{{dsc begin}}
 
{{dsc begin}}
{{dsc see cpp | cpp/language/declarations | Declarations}}
+
{{dsc see cpp | cpp/language/declarations | Declarations | nomono=true}}
 
{{dsc end}}
 
{{dsc end}}
  
[[ar:cpp/language/declarations]]
+
{{langlinks|ar|cs|de|es|fr|it|ja|ko|pl|pt|ru|tr|zh}}
[[cs:cpp/language/declarations]]
+
[[de:cpp/language/declarations]]
+
[[es:cpp/language/declarations]]
+
[[fr:cpp/language/declarations]]
+
[[it:cpp/language/declarations]]
+
[[ja:cpp/language/declarations]]
+
[[ko:cpp/language/declarations]]
+
[[pl:cpp/language/declarations]]
+
[[pt:cpp/language/declarations]]
+
[[ru:cpp/language/declarations]]
+
[[tr:cpp/language/declarations]]
+
[[zh:cpp/language/declarations]]
+

Latest revision as of 14:10, 22 June 2023

A declaration is a C language construct that introduces one or more identifiers into the program and specifies their meaning and properties.

Declarations may appear in any scope. Each declaration ends with a semicolon (just like a statement) and consists of two(until C23)three(since C23) distinct parts:

specifiers-and-qualifiers declarators-and-initializers(optional) ; (1)
attr-spec-seq specifiers-and-qualifiers declarators-and-initializers ; (2) (since C23)
attr-spec-seq ; (3) (since C23)

where

specifiers-and-qualifiers - whitespace-separated list of, in any order,
  • type specifiers:
declarators-and-initializers - comma-separated list of declarators (each declarator provides additional type information and/or the identifier to declare). Declarators may be accompanied by initializers. The enum, struct, and union declarations may omit declarators, in which case they only introduce the enumeration constants and/or tags.
attr-spec-seq - (C23)optional list of attributes, applied to the declared entities, or forms an attribute declaration if appears alone.
1-2) Simple declaration. Introduces one or more identifiers which denotes objects, functions, struct/union/enum tags, typedefs, or enumeration constants.
3) Attribute declaration. Does not declares any identifier, and has implementation-defined meaning if the meaning is not specified by the standard.

For example,

int a, *b=NULL; // "int" is the type specifier,
                // "a" is a declarator
                // "*b" is a declarator and NULL is its initializer
const int *f(void); // "int" is the type specifier
                    // "const" is the type qualifier
                    // "*f(void)" is the declarator
enum COLOR {RED, GREEN, BLUE} c; // "enum COLOR {RED, GREEN, BLUE}" is the type specifier
                                 // "c" is the declarator

The type of each identifier introduced in a declaration is determined by a combination of the type specified by the type specifier and the type modifications applied by its declarator. The type of a variable might also be inferred if auto specifier is used.(since C23)

Attributes(since C23) may appear in specifiers-and-qualifiers, in which case they apply to the type determined by the preceding specifiers.

Contents

[edit] Declarators

Each declarator is one of the following:

identifier attr-spec-seq(optional) (1)
( declarator ) (2)
* attr-spec-seq(optional) qualifiers(optional) declarator (3)
noptr-declarator [ static(optional) qualifiers(optional) expression ]

noptr-declarator [ qualifiers(optional) * ]

(4)
noptr-declarator ( parameters-or-identifiers ) (5)
1) the identifier that this declarator introduces.
2) any declarator may be enclosed in parentheses; this is required to introduce pointers to arrays and pointers to functions.
3) pointer declarator: the declaration S * cvr D; declares D as a cvr-qualified pointer to the type determined by S.
4) array declarator: the declaration S D[N] declares D as an array of N objects of the type determined by S. noptr-declarator is any other declarator except unparenthesized pointer declarator.
5) function declarator: the declaration S D(params) declared D as a function taking the parameters params and returning S. noptr-declarator is any other declarator except unparenthesized pointer declarator.

The reasoning behind this syntax is that when the identifier declared by the declarator appears in an expression of the same form as the declarator, it would have the type specified by the type specifier sequence.

struct C {
    int member; // "int" is the type specifier 
                // "member" is the declarator
} obj, *pObj = &obj;
// "struct C { int member; }" is the type specifier
// declarator "obj" defines an object of type struct C
// declarator "*pObj" declares a pointer to C,
// initializer "= &obj" provides the initial value for that pointer
 
int a = 1, *p = NULL, f(void), (*pf)(double);
// the type specifier is "int"
// declarator "a" defines an object of type int
//   initializer "=1" provides its initial value
// declarator "*p" defines an object of type pointer to int
//   initializer "=NULL" provides its initial value
// declarator "f(void)" declares a function taking void and returning int
// declarator "(*pf)(double)" defines an object of type pointer
//   to function taking double and returning int
 
int (*(*foo)(double))[3] = NULL;
// the type specifier is int
// 1. declarator "(*(*foo)(double))[3]" is an array declarator:
//    the type declared is "/nested declarator/ array of 3 int"
// 2. the nested declarator is "*(*foo)(double))", which is a pointer declarator
//    the type declared is "/nested declarator/ pointer to array of 3 int"
// 3. the nested declarator is "(*foo)(double)", which is a function declarator
//    the type declared is "/nested declarator/ function taking double and returning
//        pointer to array of 3 int"
// 4. the nested declarator is "(*foo)" which is a (parenthesized, as required by
//        function declarator syntax) pointer declarator.
//    the type declared is "/nested declarator/ pointer to function taking double
//        and returning pointer to array of 3 int"
// 5. the nested declarator is "foo", which is an identifier.
// The declaration introduces the identifier "foo" to refer to an object of type
// "pointer to function taking double and returning pointer to array of 3 int"
// The initializer "= NULL" provides the initial value of this pointer.
 
// If "foo" is used in an expression of the form of the declarator, its type would be
// int.
int x = (*(*foo)(1.2))[0];

The end of every declarator that is not part of another declarator is a sequence point.

In all cases, attr-spec-seq is an optional sequence of attributes(since C23). When appearing immediately after the identifier, it applies to the object or function being declared.

[edit] Definitions

A definition is a declaration that provides all information about the identifiers it declares.

Every declaration of an enum or a typedef is a definition.

For functions, a declaration that includes the function body is a function definition:

int foo(double); // declaration
int foo(double x){ return x; } // definition

For objects, a declaration that allocates storage (automatic or static, but not extern) is a definition, while a declaration that does not allocate storage (external declaration) is not.

extern int n; // declaration
int n = 10; // definition

For structs and unions, declarations that specify the list of members are definitions:

struct X; // declaration
struct X { int n; }; // definition

[edit] Redeclaration

A declaration cannot introduce an identifier if another declaration for the same identifier in the same scope appears earlier, except that

  • Declarations of objects with linkage (external or internal) can be repeated:
extern int x;
int x = 10; // OK
extern int x; // OK
 
static int n;
static int n = 10; // OK
static int n; // OK
  • Non-VLA typedef can be repeated as long as it names the same type:
typedef int int_t; 
typedef int int_t; // OK
struct X;
struct X { int n; };
struct X;

These rules simplify the use of header files.

[edit] Notes

In C89, declarations within any compound statement (block scope) must appear in the beginning of the block, before any statements.

Also, in C89, functions returning int may be implicitly declared by the function call operator and function parameters of type int do not have to be declared when using old-style function definitions.

(until C99)

Empty declarators are prohibited; a simple declaration must have at least one declarator or declare at least one struct/union/enum tag, or introduce at least one enumeration constant.

If any part of a declarator is a variable-length array (VLA) declarator, the entire declarator's type is known as "variably-modified type". Types defined from variably-modified types are also variably modified (VM).

Declarations of any variably-modified types may appear only at block scope or function prototype scope and cannot be members of structs or unions. Although VLA can only have automatic or allocated storage duration, a VM type such as a pointer to a VLA may be static. There are other restrictions on the use of VM types, see goto, switch. longjmp

(since C99)

static_asserts are considered to be declarations from the point of view of the C grammar (so that they may appear anywhere a declaration may appear), but they do not introduce any identifiers and do not follow the declaration syntax.

(since C11)

Attribute declarations are also considered to be declarations (so that they may appear anywhere a declaration may appear), but they do not introduce any identifiers. A single ; without attr-spec-seq is not an attribute declaration, but a statement.

(since C23)

[edit] References

  • C17 standard (ISO/IEC 9899:2018):
  • 6.7 Declarations (p: 78-105)
  • C11 standard (ISO/IEC 9899:2011):
  • 6.7 Declarations (p: 108-145)
  • C99 standard (ISO/IEC 9899:1999):
  • 6.7 Declarations (p: 97-130)
  • C89/C90 standard (ISO/IEC 9899:1990):
  • 3.5 Declarations

[edit] See also

C++ documentation for Declarations