Spec-Zone.ru › C

Объявления

Объявление — это конструкция языка 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 - список, разделенный пробелами, в любом порядке,
  • спецификаторы типов:
    • void
    • имя арифметического типа
    • имя атомного типа
    • имя, ранее введенное объявлением typedef
    • спецификатор struct, union или enum
    • спецификатор typeof (с C23)
  • нулевой или один спецификатор класса хранения: typedef, constexpr, auto, register, static, extern, _Thread_local
  • нулевые или более квалификаторы типа: const, volatile, restrict, _Atomic
  • (только при объявлении функций), нулевые или более спецификаторы функций: inline, _Noreturn
  • нулевые или более спецификаторы выравнивания: _Alignas
declarators-and-initializers - список, разделенный запятыми, деклараторов (каждый декларатор предоставляет дополнительную информацию о типе и/или идентификатор для объявления). Деклараторы могут сопровождаться инициализаторами. Объявления перечислений, структур и объединений могут опустить деклараторы, в этом случае они только вводят константы перечислений и/или теги.
attr-spec-seq - (C23) необязательный список атрибутов, применяемых к объявленным сущностям, или образует объявление атрибута, если оно встречается самостоятельно.
1-2) Простое объявление. Вводит один или несколько идентификаторов, обозначающих объекты, функции, теги struct/union/enum, typedef или константы перечислений.
3) Объявление атрибута. Не объявляет ни одного идентификатора и имеет смысл, определяемый реализацией, если смысл не указан стандартом.

Например,

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 [ qualifiers(необязательно) * ]

(4)
noptr-declarator ( parameters-or-identifiers ) (5)
1) идентификатор, вводимый этим декларатором.
2) любой декларатор может быть заключён в скобки; это необходимо для введения указателей на массивы и указателей на функции.
3) декларатор указателя: объявление S * cvr D; объявляет D как квалифицированный указатель на тип, определяемый S.
4) декларатор массива: объявление S D[N] объявляет D как массив из N объектов типа, определяемого S. noptr-declarator — это любой другой декларатор, кроме нескобкового декларатора указателя.
5) декларатор функции: объявление S D(params) объявляет D как функцию, принимающую параметры params и возвращающую S. noptr-declarator — это любой другой декларатор, кроме нескобкового декларатора указателя.

Причина такой синтаксической конструкции заключается в том, что когда идентификатор, объявленный декларатором, появляется в выражении той же формы, что и декларатор, он имеет тип, указанный последовательностью спецификаторов типа.

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];

Конец каждого декларатора, который не является частью другого декларатора, является точкой последовательности.

Во всех случаях attr-spec-seq — это необязательная последовательность атрибутов(с C23). При появлении сразу после идентификатора он применяется к объявляемому объекту или функции.

Определения

Определение — это объявление, которое предоставляет всю информацию об объявляемых им идентификаторах.

Любое объявление перечисления или 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
  • Не-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_assert считаются объявлениями с точки зрения грамматики C (так что они могут появляться там, где может появляться объявление), но не вводят никаких идентификаторов и не следуют синтаксису объявления.

(с C11)

Объявления атрибутов также считаются объявлениями (так что они могут появляться там, где может появляться объявление), но не вводят никаких идентификаторов. Один ; без attr-spec-seq не является объявлением атрибута, а оператором.

(с C23)

Ссылки

  • Стандарт 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/w/c/language/declarations

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API