Классы хранения в C: auto, register, static, extern — область видимости и время жизни
Что такое класс хранения?
Класс хранения (storage class) определяет:
- Область видимости (scope) — где в коде имя доступно.
- Время жизни (lifetime) — когда память выделяется и освобождается.
- Линковку (linkage) — видно ли имя другим файлам компиляции.
- Место хранения — стек, регистр, сегмент данных (BSS/DATA).
В C есть 4 класса хранения (плюс _Thread_local в C11):
1. auto (автоматический) — по умолчанию для локальных
void func() {
int a; // auto неявно
auto int b; // явно (редко используется)
}
- Область: блок
{...}. - Жизнь: вход в блок → выход из блока (стек).
- Линковка: нет (не видно вне блока).
- Инициализация: неавтоматически (мусор).
- Адрес: можно взять (
&a).
Примечание: в C++11 auto означает «вывод типа», в C — только класс хранения.
2. register — подсказка для регистра процессора
void func() {
register int counter = 0; // просьба положить в регистр
for (int i = 0; i < 100; i++) counter += i;
}
- Область/Жизнь/Линковка: как
auto. - Ограничение: нельзя взять адрес (
&counter— ошибка компиляции). - Реальность: современные компиляторы (GCC, Clang) сами оптимально распределяют регистры. Ключевое слово игнорируется или используется как хинт. Используйте редко.
3. static — статическое хранение
Два разных контекста — разное поведение:
А) static локальная переменная (внутри функции)
void counter() {
static int count = 0; // инициализация ТОЛЬКО при первом вызове
count++;
printf("Call %d\n", count);
}
- Область: блок функции.
- Жизнь: вся программа (память в .data/.bss).
- Инициализация: один раз (константное выражение или константа), до
main. Если не инициализирована — 0. - Применение: сохранение состояния между вызовами, кэши, одиночки (singletons).
Б) static глобальная переменная / функция (вне функций)
// file1.c
static int internal_var = 42; // видно ТОЛЬКО в file1.c
static void internal_func() {} // внутренняя линковка
- Область: файл (translation unit).
- Линковка: внутренняя (internal) — не видно линкеру из других файлов.
- Применение: инкапсуляция внутри модуля, избежание конфликтов имен.
4. extern — внешняя линковка
Объявляет, что переменная/функция определена в другом файле.
Переменные
// main.c
int global_config = 100; // определение (память выделена здесь)
extern void init_subsystem(); // объявление функции из другого файла
// subsystem.c
extern int global_config; // объявление: используем ту же память
void init_subsystem() {
global_config = 200; // меняем общую переменную
}
- Определение — ровно в одном файле (без
externили с инициализацией). - Объявления — в заголовочных файлах (
.h) сextern. - Инициализация: в определении.
extern int x = 5;— это определение!
Функции
По умолчанию функции имеют extern линковку. extern void foo(); и void foo(); эквивалентны. static void foo(); — внутренняя.
Сводная таблица
| Класс | Ключевое слово | Где объявляется | Область видимости | Время жизни | Линковка | Инициализация по умолч. |
|---|---|---|---|---|---|---|
| Авто | auto (неявно) | Блок | Блок | Блок | Нет | Неопределённо |
| Регистр | register | Блок | Блок | Блок | Нет | Неопределённо |
| Статик (лок.) | static | Блок | Блок | Программа | Нет | 0 |
| Статик (глоб.) | static | Файл | Файл | Программа | Внутренняя | 0 |
| Экстерн | extern | Файл / заголовочник | Программа | Программа | Внешняя | 0 (в определении) |
Пример: многомодульный проект
// config.h
#ifndef CONFIG_H
#define CONFIG_H
extern int max_connections; // объявление
extern const char *app_version; // объявление
void config_init(void);
#endif
// config.c
#include "config.h"
int max_connections = 10; // определение
const char *app_version = "1.0"; // определение
void config_init() { max_connections = 100; }
// main.c
#include <stdio.h>
#include "config.h"
int main() {
config_init();
printf("Max connections: %d, Version: %s\n", max_connections, app_version);
return 0;
}
Компиляция: gcc main.c config.c -o app.
C11: _Thread_local
_Thread_local int thread_id = 0; // каждый поток имеет свою копию
- Время жизни: от создания потока до его завершения.
- Аналог
thread_localв C++11. - Требует
<threads.h>и поддержки компилятора.
Лучшие практики
- Минимизируйте глобальные переменные. Используйте
staticдля файл-локальных. - Объявляйте
externв.h, определяйте в.c. - Используйте
staticдля функций, не нужных вне модуля. - Избегайте
register— компилятор умнее. - Для констант:
constилиenum, не#define(кроме условной компиляции). - Потокобезопасность:
staticлокальные в функциях — не потокобезопасны при инициализации (C11 гарантирует atomic init, но не использование).
Язык C
- Классы и объекты в C++: практическое руководство по объектно-ориентированному программированию
- Дружественные функции и классы в C++: мастерство контроля доступа
- Классы хранения в C: область видимости, время жизни и производительность
- Структуры C++ против классов: практическое руководство для разработчиков встроенных систем
- Мастерство метода str.count() в Python: как подсчитывать символы и подстроки с примерами
- Python list.count(): экспертное руководство с практическими примерами
- Java: объекты, классы и основные концепции ООП
- Классы хранения в C++: область видимости, время жизни и использование
- Интерфейсы в C++: абстрактные классы и чисто виртуальные функции
- Классы в C#: определение, конструкторы, деструкторы, статические члены