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(начиная с C11)(до C23)alignas(начиная с C23)
declarators-and-initializers - список деклараторов, разделённых запятыми (каждый декларатор предоставляет дополнительные сведения о типе и/или идентификатор, который нужно объявить). Деклараторы могут сопровождаться инициализаторами. Объявления enum, struct и union могут не содержать деклараторов; в таком случае они вводят только константы перечисления и/или теги.
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 как указатель с квалификаторами cvr на тип, определённый S.
4) декларатор массива: объявление S D[N] объявляет D как массив из N объектов типа, определённого S. noptr-declarator — любой другой декларатор, кроме деклараторa указателя, не заключённого в скобки.
5) декларатор функции: объявление 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 и union могут повторяться:
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. longjmp

(начиная с C99)

static_assert считаются объявлениями с точки зрения грамматики C (поэтому они могут встречаться везде, где допустимы объявления), но не вводят никаких идентификаторов и не соответствуют синтаксису объявления.

(начиная с C11)

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

(начиная с 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

Spec-Zone.ru

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