Namespaces
Variants

Storage-class specifiers

From cppreference.net

Укажите storage duration и linkage объектов и функций:

  • auto - автоматическая продолжительность и отсутствие связывания
  • register - автоматическая продолжительность и отсутствие связывания; адрес этой переменной не может быть взят
  • static - статическая продолжительность и внутреннее связывание (если не находится в области видимости блока)
  • extern - статическая продолжительность и внешнее связывание (если уже не объявлено как внутреннее)
  • _Thread_local (до C23) thread_local (начиная с C23) - продолжительность хранения потока
(начиная с C11)

Содержание

Объяснение

Спецификаторы класса хранения появляются в объявлениях и выражениях составных литералов (начиная с C23) . Может использоваться не более одного спецификатора , за исключением того, что _Thread_local (до C23) thread_local (начиная с C23) может комбинироваться с static или extern для корректировки связывания (начиная с C11) . Спецификаторы класса хранения определяют два независимых свойства объявляемых имён: время хранения и связывание .

1) Спецификатор auto разрешен только для объектов, объявленных в области видимости блока (за исключением списков параметров функции). Он указывает на автоматическую продолжительность хранения и отсутствие связывания, что является стандартным поведением для таких объявлений.
2) Спецификатор register разрешён только для объектов, объявленных в области видимости блока, включая списки параметров функций. Он указывает на автоматическую продолжительность хранения и отсутствие связывания (что является стандартным для таких объявлений), но дополнительно подсказывает оптимизатору сохранить значение этой переменной в регистре процессора, если это возможно. Независимо от того, применяется ли эта оптимизация, переменные, объявленные с register , не могут использоваться в качестве аргументов для оператора взятия адреса , не могут использовать _Alignas (до C23) alignas (начиная с C23) (начиная с C11) , и register массивы не преобразуются в указатели.
3) Спецификатор static определяет как статическую продолжительность хранения (если не используется в сочетании с _Thread_local ) (начиная с C11) так и внутреннюю линковку (если не используется в области видимости блока). Он может применяться к функциям на уровне файла и к переменным как на уровне файла, так и в области видимости блока, но не в списках параметров функций.
4) Спецификатор extern указывает на статическую продолжительность хранения (если не используется совместно с _Thread_local (до C23) thread_local (начиная с C23) ) (начиная с C11) и внешнюю линковку. Может использоваться в объявлениях функций и объектов как на уровне файла, так и в области видимости блока (за исключением списков параметров функций). Если extern встречается в повторном объявлении идентификатора, который уже был объявлен с внутренней линковкой, линковка остается внутренней. В остальных случаях (если предыдущее объявление имело внешнюю линковку, отсутствие линковки или находится вне области видимости) линковка является внешней.
5) _Thread_local (до C23) thread_local (начиная с C23) указывает на продолжительность хранения в потоке . Не может использоваться с объявлениями функций. Если используется при объявлении объекта, должно присутствовать в каждом объявлении того же объекта. Если используется в объявлении с областью видимости блока, должно комбинироваться либо с static , либо с extern для определения линковки.
(начиная с C11)

Если спецификатор класса хранения не указан, значения по умолчанию следующие:

extern для всех функций
extern для объектов на уровне файла
auto для объектов на уровне блока

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

Объявления функций на уровне блока могут использовать extern или не использовать ничего. Объявления функций на уровне файла могут использовать extern или static .

Параметры функций не могут использовать спецификаторы класса хранения, кроме register . Обратите внимание, что static имеет особое значение в параметрах функций типа массив.

Продолжительность хранения

Каждый объект обладает свойством, называемым продолжительность хранения , которое ограничивает время жизни объекта. В языке C существует четыре вида продолжительности хранения:

  • automatic продолжительность хранения. Память выделяется при входе в блок , в котором объявлен объект, и освобождается при выходе из него любым способом ( goto , return , достижение конца). Исключением являются VLA ; их память выделяется при выполнении объявления, а не при входе в блок, и освобождается при выходе объявления из области видимости, а не при выходе из блока (начиная с C99) . Если блок входит рекурсивно, новое выделение выполняется для каждого уровня рекурсии. Все параметры функций и не- static объекты блочной области видимости имеют эту продолжительность хранения, а также составные литералы , используемые в блочной области видимости (до C23)
  • static продолжительность хранения. Продолжительность хранения охватывает всё время выполнения программы, и значение, хранящееся в объекте, инициализируется только один раз, до вызова функции main . Все объекты, объявленные как static , и все объекты с внутренней или внешней линковкой которые не объявлены _Thread_local (до C23) thread_local (начиная с C23) (начиная с C11) имеют эту продолжительность хранения.
  • thread storage duration. Продолжительность хранения - это все время выполнения потока, в котором он был создан, и значение, хранящееся в объекте, инициализируется при запуске потока. Каждый поток имеет свой собственный, уникальный объект. Если поток, выполняющий выражение, которое обращается к этому объекту, не является потоком, выполнившим его инициализацию, поведение определяется реализацией. Все объекты, объявленные _Thread_local (until C23) thread_local (since C23) имеют эту продолжительность хранения.
(since C11)

Связывание

Связывание (linkage) относится к способности идентификатора (переменной или функции) быть упомянутым в других областях видимости. Если переменная или функция с одинаковым идентификатором объявлена в нескольких областях видимости, но не может быть упомянута из всех них, то создаются несколько экземпляров переменной. Различают следующие виды связывания:

  • отсутствие связи . На переменную или функцию можно ссылаться только из той области видимости, в которой она находится (область видимости блока). Все переменные области видимости блока, которые не объявлены как extern , имеют эту связь, а также все параметры функций и все идентификаторы, не являющиеся функциями или переменными.
  • внутренняя линковка . Переменная или функция может быть использована из всех областей видимости в текущей единице трансляции. Все переменные области видимости файла, объявленные как static или constexpr (начиная с C23) , имеют эту линковку, как и все функции области видимости файла, объявленные как static (объявления статических функций разрешены только в области видимости файла).
  • внешняя компоновка . Переменная или функция может быть использована из любых других единиц трансляции во всей программе. Все переменные области видимости файла, которые не объявлены static или constexpr (начиная с C23) , имеют эту компоновку, все объявления функций области видимости файла, которые не объявлены static , все объявления функций области видимости блока, и, дополнительно, все переменные или функции, объявленные extern , имеют эту компоновку, если в данной точке не видно предыдущее объявление с внутренней компоновкой.

Если один и тот же идентификатор появляется одновременно с внутренней и внешней линковкой в одной единице трансляции, поведение не определено. Это возможно при использовании tentative definitions .

Линковка и библиотеки

Объявления с внешней линковкой обычно предоставляются в заголовочных файлах, чтобы все единицы трансляции, которые #include этот файл, могли ссылаться на один и тот же идентификатор, определённый в другом месте.

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

Интерфейс библиотеки, заголовочный файл "flib.h":

#ifndef FLIB_H
#define FLIB_H
void f(void);              // объявление функции с внешней линковкой
extern int state;          // объявление переменной с внешней линковкой
static const int size = 5; // определение переменной только для чтения с внутренней линковкой
enum { MAX = 10 };         // определение константы
inline int sum (int a, int b) { return a + b; } // определение встроенной функции
#endif // FLIB_H

Реализация библиотеки, исходный файл "flib.c":

#include "flib.h"
static void local_f(int s) {} // определение с внутренней линковкой (используется только в этом файле)
static int local_state;       // определение с внутренней линковкой (используется только в этом файле)
int state;                       // определение с внешней линковкой (используется в main.c)
void f(void) { local_f(state); } // определение с внешней линковкой (используется в main.c)

Код приложения, исходный файл "main.c":

#include "flib.h"
int main(void)
{
    int x[MAX] = {size}; // использует константу и переменную только для чтения
    state = 7;           // изменяет state в flib.c
    f();                 // вызывает f() в flib.c
}

Ключевые слова

auto , register , static , extern , _Thread_local thread_local

Примечания

Ключевое слово _Thread_local обычно используется через удобный макрос thread_local , определённый в заголовочном файле <threads.h> .

(до C23)

Спецификаторы typedef и constexpr (начиная с C23) формально перечислены как спецификаторы класса хранения в грамматике языка C, но не определяют хранение.

Спецификатор auto также используется для вывода типа.

(since C23)

Имена на уровне файла, которые являются const и не extern имеют внешнюю линковку в C (как значение по умолчанию для всех объявлений на уровне файла), но внутреннюю линковку в C++.

Пример

#include <stdio.h>
#include <stdlib.h>
// static storage duration
int A;
int main(void)
{
    printf("&A = %p\n", (void*)&A);
    // automatic storage duration
    int A = 1;   // hides global A
    printf("&A = %p\n", (void*)&A);
    // allocated storage duration
    int* ptr_1 = malloc(sizeof(int));   // start allocated storage duration
    printf("address of int in allocated memory = %p\n", (void*)ptr_1);
    free(ptr_1);                        // stop allocated storage duration
}

Возможный вывод:

&A = 0x600ae4
&A = 0x7ffefb064f5c
address of int in allocated memory = 0x1f28c30

Ссылки

  • Стандарт C23 (ISO/IEC 9899:2024):
  • 6.2.2 Связывания идентификаторов (стр: 35-36)
  • 6.2.4 Время хранения объектов (стр: 36-37)
  • 6.7.1 Спецификаторы класса хранения (стр: 97-100)
  • Стандарт C17 (ISO/IEC 9899:2018):
  • 6.2.2 Связывания идентификаторов (стр: 29-30)
  • 6.2.4 Время хранения объектов (стр: 30)
  • 6.7.1 Спецификаторы класса памяти (стр: 79)
  • Стандарт C11 (ISO/IEC 9899:2011):
  • 6.2.2 Связывания идентификаторов (стр: 36-37)
  • 6.2.4 Время жизни объектов (стр: 38-39)
  • 6.7.1 Спецификаторы класса памяти (стр: 109-110)
  • Стандарт C99 (ISO/IEC 9899:1999):
  • 6.2.2 Связывания идентификаторов (стр: 30-31)
  • 6.2.4 Время хранения объектов (стр: 32)
  • 6.7.1 Спецификаторы класса памяти (стр: 98-99)
  • Стандарт C89/C90 (ISO/IEC 9899:1990):
  • 3.1.2.2 Связывания идентификаторов
  • 3.1.2.4 Время хранения объектов
  • 3.5.1 Спецификаторы класса памяти

Смотрите также

C++ documentation for Storage class specifiers