Объявления
Объявление — это конструкция языка C, которая вводит в программу один или несколько идентификаторов и задаёт их смысл и свойства.
Объявления могут встречаться в любой области видимости. Каждое объявление заканчивается точкой с запятой (как и оператор) и состоит из двух(до C23)трёх(начиная с C23) отдельных частей:
specifiers-and-qualifiers declarators-and-initializers (необязательно) ; | (1) | |
attr-spec-seq specifiers-and-qualifiers declarators-and-initializers ; | (2) | (начиная с C23) |
attr-spec-seq ; | (3) | (начиная с C23) |
где
| specifiers-and-qualifiers | - | разделённый пробелами список в любом порядке из
|
| declarators-and-initializers | - | список деклараторов, разделённых запятыми (каждый декларатор предоставляет дополнительные сведения о типе и/или идентификатор, который нужно объявить). Деклараторы могут сопровождаться инициализаторами. Объявления enum, struct и union могут не содержать деклараторов; в таком случае они вводят только константы перечисления и/или теги. |
| attr-spec-seq | - | (C23)необязательный список атрибутов, применяемых к объявляемым сущностям; если он встречается сам по себе, то образует объявление атрибутов. |
Например,
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
Тип каждого идентификатора, введённого в объявлении, определяется сочетанием типа, заданного спецификатором типа, и изменений типа, применённых его декларатором. Тип переменной также может выводиться, если используется спецификатор auto.(начиная с C23)
Атрибуты(начиная с C23) могут встречаться в specifiers-and-qualifiers; в этом случае они применяются к типу, определённому предшествующими спецификаторами.
Деклараторы
Каждый декларатор является одним из следующих:
| identifier attr-spec-seq (необязательно) | (1) | |
( declarator ) | (2) | |
* attr-spec-seq (необязательно) qualifiers (необязательно) declarator | (3) | |
noptr-declarator [ static(необязательно) qualifiers (необязательно) expression ] noptr-declarator | (4) | |
noptr-declarator ( parameters-or-identifiers ) | (5) |
S * cvr D; объявляет D как указатель с квалификаторами cvr на тип, определённый S.S D[N] объявляет D как массив из N объектов типа, определённого S. noptr-declarator — любой другой декларатор, кроме деклараторa указателя, не заключённого в скобки.S D(params) объявляет D как функцию, принимающую параметры params и возвращающую S. noptr-declarator — любой другой декларатор, кроме деклараторa указателя, не заключённого в скобки.Смысл этого синтаксиса в том, что если идентификатор, объявленный декларатором, встречается в выражении той же формы, что и декларатор, то он будет иметь тип, указанный последовательностью спецификаторов типа.
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];
Конец каждого деклараторa, не являющегося частью другого деклараторa, является точкой следования.
Во всех случаях attr-spec-seq представляет собой необязательную последовательность атрибутов(начиная с C23). Если они расположены непосредственно после идентификатора, то применяются к объявляемому объекту или функции.
Определения
Определение — это объявление, предоставляющее всю информацию об объявляемых им идентификаторах.
Каждое объявление enum или typedef является определением.
Для функций объявление, включающее тело функции, является определением функции:
int foo(double); // declaration
int foo(double x) { return x; } // definition
Для объектов объявление, выделяющее память (автоматическую или статическую, но не extern), является определением, тогда как объявление, не выделяющее память (внешнее объявление), таковым не является.
extern int n; // declaration int n = 10; // definition
Для структур и объединений объявления, задающие список членов, являются определениями:
struct X; // declaration
struct X { int n; }; // definition
Повторное объявление
Объявление не может вводить идентификатор, если в той же области видимости ранее встречалось другое объявление этого идентификатора, за исключением следующих случаев:
- Объявления объектов со связыванием (внешним или внутренним) могут повторяться:
extern int x; int x = 10; // OK extern int x; // OK static int n; static int n = 10; // OK static int n; // OK
- Объявления typedef без VLA могут повторяться, если они обозначают один и тот же тип:
typedef int int_t; typedef int int_t; // OK
struct X;
struct X { int n; };
struct X;
Эти правила упрощают использование заголовочных файлов.
Примечания
| В C89 объявления внутри любого составного оператора (в области видимости блока) должны находиться в начале блока, до любых операторов. Кроме того, в C89 функции, возвращающие int, могут неявно объявляться оператором вызова функции, а параметры функции типа int можно не объявлять при использовании устаревших определений функций. | (до C99) |
Пустые деклараторы запрещены; простое объявление должно содержать как минимум один декларатор или объявлять как минимум один тег struct/union/enum либо вводить как минимум одну константу перечисления.
| Если какая-либо часть деклараторa является декларатором массива переменной длины (VLA), тип всего деклараторa называется «типом с переменной модификацией». Типы, определённые на основе типов с переменной модификацией, также имеют переменную модификацию (VM). Объявления любых типов с переменной модификацией могут встречаться только в области видимости блока или области видимости прототипа функции и не могут быть членами структур или объединений. Хотя VLA может иметь только автоматическую или выделенную длительность хранения, тип VM, например указатель на VLA, может быть статическим. На использование типов VM накладываются и другие ограничения; см. goto, switch. | (начиная с C99) |
| static_assert считаются объявлениями с точки зрения грамматики C (поэтому они могут встречаться везде, где допустимы объявления), но не вводят никаких идентификаторов и не соответствуют синтаксису объявления. | (начиная с C11) |
| Объявления атрибутов также считаются объявлениями (поэтому они могут встречаться везде, где допустимы объявления), но не вводят никаких идентификаторов. Одиночный | (начиная с C23) |
Ссылки
- Стандарт C23 (ISO/IEC 9899:2024):
- 6.7 Объявления (стр.: TBD)
- Стандарт C17 (ISO/IEC 9899:2018):
- 6.7 Объявления (стр.: 78–105)
- Стандарт C11 (ISO/IEC 9899:2011):
- 6.7 Объявления (стр.: 108–145)
- Стандарт C99 (ISO/IEC 9899:1999):
- 6.7 Объявления (стр.: 97–130)
- Стандарт C89/C90 (ISO/IEC 9899:1990):
- 3.5 Объявления
См. также
| Документация по C++ для объявлений |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/c/language/declarations