Классы памяти
Класс памяти определяет способ размещения переменной в памяти и ее доступность в различных частях программы. На практике чаще всего определения как локальных, так и глобальных переменных не включают явно соответствующих модификаторов, но спецификацией языка C предусмотрены следующие классы памяти:
auto – так называемая автоматическая переменная, размещаемая в оперативной памяти компьютера. Этот класс памяти используется по умолчанию, если иное не указано явно при описании переменной. Локальные переменные располагаются в сегменте стека, память для них выделяется на вершине стека автоматически при входе в функцию или блок и освобождается автоматически при выходе из функции или блока. Для глобальных переменных модификатор auto не применим, так как определяет переменную с локальным временем жизни. На практике модификатор auto используется крайне редко из-за отсутствия необходимости в нем.
register – регистровая переменная. Отличие от автоматической заключается в том, что переменная располагается в регистрах процессора. Это используется для ускорения доступа к переменной, однако, является скорее просьбой, чем директивой – компилятору не всегда удается выделить регистр процессора для хранения переменной из-за: малого количества регистров; большого количества переменных, объявленных как регистровые; большого размера типа данных этой переменной; если регистровые переменные запрещены параметрами компилятора. Регистровой может быть только локальная переменная, но к ней не применима операция определения адреса. В примере слева показано правильное использование регистровой переменной i для ускорения работы цикла, а справа – неправильное, так как попытка определить адрес регистровой переменной x в выражении &x при вызове функции scanf приведет к ошибке при компиляции:
register int i; |
register int x; |
... |
... |
for (i = 0; i < n; i++) |
scanf("%d", &x); |
... |
... |
static – статическая переменная. Такая переменная имеет глобальное время жизни, располагается в той же области памяти, что и глобальные переменные и инициализируется однократно вначале выполнения программы (до первого использования). По сути, статические переменные являются глобальными (располагаются в сегменте данных и память для них выделяется при запуске программы), однако область действия статической переменной ограничена той областью, в которой она определена. Таким образом, глобальная статическая переменная окажется «спрятанной» внутри модуля и не будет доступна в других модулях программы (это используется для уменьшения количества идентификаторов в глобальном пространстве). Локальная статическая переменная будет доступна только в своем блоке (включая вложенные в него), но не будет уничтожаться при выходе из блока и сохранит свое значение до следующего входа в него (например, в циклах или в функциях). В следующем примере переменная x при каждом выполнении блока (например, в цикле) получает значение, равное среднему между текущим и предыдущим ((p + x) / 2), причем текущее значение сохраняется в статической переменной p и, следовательно, может использоваться только в этом блоке:
double x;
...
{
static double p = 0.0;
double t; |
/* переменная для временного хранения */ |
t = (p + x) / 2; |
/* результата */ |
p = x; |
/* сохранить значение "на будущее" */ |
x = t; |
/* присвоить результат */ |
} |
|
... |
|
extern – внешняя переменная. Только при использовании данного модификатора, описание переменной является объявлением, во всех остальных случаях – определением. Модификатор extern указывает на то, что память для этой переменной выделяется где-то в другом месте программы при ее определении, а сама переменная при этом должна быть определена как глобальная и не статическая. Таким образом, описание внешней переменной является своеобразной ссылкой на «настоящую» переменную. Это позволяет расширить область действия глобальной переменной и обращаться к ней в другом модуле, либо в том же модуле, но до ее определения (выше по тексту). В последнем случае эта переменная может быть определена и как статическая, так как она используется в пределах одного модуля:
#include <stdio.h> |
#include <stdio.h> |
void main(void) |
void main(void) |
{ |
{ |
int k = 1; |
int k = 1; |
{ |
{ |
extern int x; |
extern int x; |
k = x; |
k = x; |
} |
} |
printf("%d\n", k); |
printf("%d\n", k); |
} |
} |
int x = 5; |
static int x = 5; |
Оба приведенных выше примера правильные и при их выполнении на экране должно быть напечатано «5». Переменная x используется во вложенном блоке и объявлена в нем как внешняя, но за его пределами в теле функции main она недоступна. Различие приведенных примеров заключается в том, что в первом случае (слева) переменная не является статической и может аналогично использоваться в других модулях программы, а во втором случае (справа) расширить ее область действия на другие модули невозможно. На практике обычно возникает необходимость расширять область действия идентификаторов, определенных в других модулях, например:
extern int errno;
void main(void)
{
... /* использование переменной errno */
}
Перечисленные выше модификаторы не могут использоваться совместно друг с другом, но не препятствуют применению других модификаторов, которые могут присутствовать в описании переменных.