Spec-Zone.ru › C

Спецификаторы класса хранения

Задают длительность хранения и связь объектов и функций:

  • 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 предусмотрено четыре вида длительности хранения:

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

Связь

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

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

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

Связь и библиотеки

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

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

Интерфейс библиотеки, заголовочный файл "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 обычно используется с помощью удобного макроса thread_local, определённого в заголовочном файле <threads.h>.

(до 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: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++ для спецификаторов класса хранения

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/c/language/storage_duration

Spec-Zone.ru

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