Спецификаторы класса хранения
Указывают длительность хранения и связь объектов и функций:
-
auto- автоматическая длительность хранения и отсутствие связи -
register- автоматическая длительность хранения и отсутствие связи; адрес этой переменной нельзя получить -
static- статическая длительность хранения и внутренняя связь (если не в области блока) -
extern- статическая длительность хранения и внешняя связь (если не объявлена как внутренняя)
| (с C11) |
Объяснение
Спецификаторы класса хранения появляются в объявлениях и выражениях сложных литералов(с C23). Может использоваться не более одного спецификатора, за исключением того, что _Thread_local(до C23)thread_local(с C23) может быть объединён с static или extern для корректировки связи(с C11). Спецификаторы класса хранения определяют две независимые свойства объявляемых ими имен: длительность хранения и связь.
| 5) _Thread_local(до C23)thread_local(с C23) указывает на длительность хранения потока. Он не может использоваться с объявлениями функций. Если он используется в объявлении объекта, он должен присутствовать в каждом объявлении того же объекта. Если он используется в объявлении в области блока, он должен быть объединён с static или extern для определения связи. | (с C11) |
Если спецификатор класса хранения не указан, значения по умолчанию:
- extern для всех функций
- extern для объектов в области файла
- auto для объектов в области блока
Для любого структуры или объединения, объявленного со спецификатором класса хранения, длительность хранения (но не связь) применяется к их членам, рекурсивно.
Объявления функций в области блока могут использовать extern или вообще ничего. Объявления функций в области файла могут использовать extern или static.
Параметры функций не могут использовать спецификаторы класса хранения, кроме register. Обратите внимание, что static имеет специальное значение в параметрах функций типа массива.
Длительность хранения
Каждый объект имеет свойство, называемое длительностью хранения, которое ограничивает жизненный цикл объекта. В C существует четыре вида длительности хранения:
- автоматическая длительность хранения. Хранение выделяется при входе в блок, в котором был объявлен объект, и освобождается при выходе из него любым способом (goto, return, достижение конца). Исключение составляют переменные длины массива (VLAs); их хранение выделяется при выполнении объявления, а не при входе в блок, и освобождается при выходе из области видимости объявления, а не при выходе из блока(с C99). Если блок вводится рекурсивно, выделение выполняется для каждого уровня рекурсии. Все параметры функций и не-static объекты в области блока имеют такую длительность хранения, а также сложные литералы, используемые в области блока(до C23)
- статическая длительность хранения. Длительность хранения — это весь период выполнения программы, и значение, хранящееся в объекте, инициализируется только один раз до функции main. Все объекты, объявленные static, и все объекты с внутренней или внешней связью которые не объявлены _Thread_local(до C23)thread_local(с C23)(с C11) имеют такую длительность хранения.
| (с C11) |
- выделенное длительность хранения. Хранение выделяется и освобождается по запросу с помощью функций динамического выделения памяти.
Связь
Связь относится к возможности ссылки на идентификатор (переменную или функцию) в других областях видимости. Если переменная или функция с тем же идентификатором объявлены в нескольких областях видимости, но не могут быть обработаны из всех них, то генерируются несколько экземпляров переменной. Признаются следующие виды связей:
- без связи. К идентификатору можно обратиться только из области, в которой он объявлен. Все параметры функций и все переменные в области блока, не объявленные как extern (включая объявленные как static), имеют такую связь.
- внутренняя связь. К идентификатору можно обратиться из всех областей видимости в текущей единице трансляции. Все идентификаторы в области файла, объявленные как static (функции и переменные), имеют такую связь.
- внешняя связь. К идентификатору можно обратиться из любой другой единицы трансляции во всей программе. Все функции, не объявленные как static, все переменные, объявленные как extern (если ранее не объявлены как static), и все нестатические переменные в области файла имеют такую связь.
Если один и тот же идентификатор появляется с внутренней и внешней связью в одной единице трансляции, поведение является неопределённым. Это возможно при использовании временных определений.
Связь и библиотеки
Декларации с внешней связью обычно размещаются в заголовочных файлах, чтобы все единицы трансляции, которые #include файл, могли ссылаться на один и тот же идентификатор, определённый где-то ещё.
Любая декларация с внутренней связью, которая появляется в заголовочном файле, приводит к созданию отдельного и отличного объекта в каждой единице трансляции, которая включает этот файл.
| Интерфейс библиотеки, заголовочный файл "flib.h": #ifndef FLIB_H
#define FLIB_H
void f(void); // function declaration with external linkage
extern int state; // variable declaration with external linkage
static const int size = 5; // definition of a read-only variable with internal linkage
enum { MAX = 10 }; // constant definition
inline int sum (int a, int b) { return a + b; } // inline function definition
#endif // FLIB_HРеализация библиотеки, исходный файл "flib.c": #include "flib.h"
static void local_f(int s) {} // definition with internal linkage (only used in this file)
static int local_state; // definition with internal linkage (only used in this file)
int state; // definition with external linkage (used by main.c)
void f(void) { local_f(state); } // definition with external linkage (used by main.c)Код приложения, исходный файл "main.c": #include "flib.h"
int main(void)
{
int x[MAX] = {size}; // uses the constant and the read-only variable
state = 7; // modifies state in flib.c
f(); // calls f() in flib.c
} |
Ключевые слова
auto, register, static, extern, _Thread_local thread_local
Примечания
| Ключевое слово _Thread_local обычно используется через макрос | (до C23) |
Метки typedef и constexpr(с C23) формально указаны как спецификаторы класса хранения в грамматике языка C, но не определяют хранение.
| Спецификатор auto также используется для вывода типа. | (с 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:2023):
- 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++ по Спецификаторам класса хранения |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/c/language/storage_duration