Declarations
Декларация — это конструкция языка 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" — спецификатор типа, // "a" — декларатор // "*b" — декларатор, а NULL — его инициализатор const int *f(void); // "int" — спецификатор типа // "const" — квалификатор типа // "*f(void)" — декларатор enum COLOR {RED, GREEN, BLUE} c; // "enum COLOR {RED, GREEN, BLUE}" — спецификатор типа // "c" — декларатор
Тип каждого идентификатора, введённого в объявлении, определяется комбинацией типа, указанного спецификатором типа и модификаций типа, применяемых его декларатором . Тип переменной также может быть выведен, если используется 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) | ||||||||
D
как
cvr
-квалифицированный указатель на тип, определяемый
S
.
D
как массив из
N
объектов типа, определяемого
S
.
noptr-declarator
- это любой другой декларатор, кроме непомещенного в скобки указательного декларатора.
D
как функцию, принимающую параметры
params
и возвращающую
S
.
noptr-declarator
— это любой другой декларатор, кроме непомещенного в скобки указательного декларатора.
Обоснование данного синтаксиса заключается в том, что когда идентификатор, объявленный декларатором, появляется в выражении той же формы, что и декларатор, он будет иметь тип, указанный в последовательности спецификаторов типа.
struct C { int member; // "int" - это спецификатор типа // "member" - это декларатор } obj, *pObj = &obj; // "struct C { int member; }" - это спецификатор типа // декларатор "obj" определяет объект типа struct C // декларатор "*pObj" объявляет указатель на C, // инициализатор "= &obj" предоставляет начальное значение для этого указателя int a = 1, *p = NULL, f(void), (*pf)(double); // спецификатор типа - "int" // декларатор "a" определяет объект типа int // инициализатор "=1" предоставляет его начальное значение // декларатор "*p" определяет объект типа указатель на int // инициализатор "=NULL" предоставляет его начальное значение // декларатор "f(void)" объявляет функцию, принимающую void и возвращающую int // декларатор "(*pf)(double)" определяет объект типа указатель // на функцию, принимающую double и возвращающую int int (*(*foo)(double))[3] = NULL; // спецификатор типа - int // 1. декларатор "(*(*foo)(double))[3]" является декларатором массива: // объявленный тип - "/вложенный декларатор/ массив из 3 int" // 2. вложенный декларатор - "*(*foo)(double))", который является декларатором указателя // объявленный тип - "/вложенный декларатор/ указатель на массив из 3 int" // 3. вложенный декларатор - "(*foo)(double)", который является декларатором функции // объявленный тип - "/вложенный декларатор/ функция, принимающая double и возвращающая // указатель на массив из 3 int" // 4. вложенный декларатор - "(*foo)", который является (в скобках, как требует // синтаксис декларатора функции) декларатором указателя. // объявленный тип - "/вложенный декларатор/ указатель на функцию, принимающую double // и возвращающую указатель на массив из 3 int" // 5. вложенный декларатор - "foo", который является идентификатором. // Объявление вводит идентификатор "foo" для ссылки на объект типа // "указатель на функцию, принимающую double и возвращающую указатель на массив из 3 int" // Инициализатор "= NULL" предоставляет начальное значение этого указателя. // Если "foo" используется в выражении в форме декларатора, его тип будет // int. int x = (*(*foo)(1.2))[0];
Конец каждого декларатора, который не является частью другого декларатора, является точкой следования .
Во всех случаях, attr-spec-seq является необязательной последовательностью атрибутов (начиная с C23) . Когда она появляется непосредственно после идентификатора, она применяется к объявляемому объекту или функции.
Определения
Определение — это объявление, которое предоставляет всю информацию об идентификаторах, которые оно объявляет.
Каждое объявление enum или typedef является определением.
Для функций объявление, которое включает тело функции, является определением функции :
int foo(double); // объявление int foo(double x) { return x; } // определение
Для объектов объявление, которое выделяет память ( автоматическая или статическая , но не extern), является определением, тогда как объявление, которое не выделяет память ( внешнее объявление ), не является определением.
extern int n; // объявление int n = 10; // определение
Для структур и объединений объявления, которые определяют список членов, являются определениями:
struct X; // объявление struct X { int n; }; // определение
Повторное объявление
Объявление не может вводить идентификатор, если другое объявление для того же идентификатора в той же области видимости появляется ранее, за исключением случаев, когда
- Объявления объектов с линковкой (внешней или внутренней) могут повторяться:
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 может повторяться до тех пор, пока он обозначает один и тот же тип:
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, или вводить как минимум одну константу перечисления.
|
Если какая-либо часть декларатора является декларатором массива переменной длины (VLA), то тип всего декларатора называется "вариативно-модифицированным типом". Типы, определенные на основе вариативно-модифицированных типов, также являются вариативно модифицированными (VM). Объявления любых вариативно-модифицированных типов могут появляться только в области видимости блока или области видимости прототипа функции и не могут быть членами структур или объединений. Хотя VLA может иметь только автоматическую или выделенную длительность хранения , VM-тип, такой как указатель на VLA, может быть статическим. Существуют другие ограничения на использование VM-типов, см. goto , switch . longjmp |
(начиная с C99) |
|
static_asserts считаются объявлениями с точки зрения грамматики C (поэтому они могут появляться везде, где может появляться объявление), но они не вводят никаких идентификаторов и не следуют синтаксису объявления. |
(since 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++ documentation
для
Declarations
|