Класс памяти определяет «время жизни» объекта. Под объектом понимается идентификатор переменной, функция либо указатель функции. Кроме этого, класс памяти в совокупности с местоположением переменной в программе, определяет область видимости переменной.
Различают два вида объектов: глобальные и локальные.
Объекты с глобальным временем жизни существуют и имеют значение на протяжении всего времени исполнения программы.
Все функции и их указатели – глобальные объекты. Локальные объекты «захватывают» новую область памяти всякий раз, когда управление передается блоку, в котором они описаны.
Если переменная описана вне всяких блоков, то она считается глобальной. Это описание на так называемом внешнем уровне.
Блок- это описание функции, составная команда, либо часть программы, заключенная в фигурные скобки
{
это блок
}
Блоки могут быть вложенными:
{
{
это вложенный блок
}
}
Переменные, описанные внутри некоторого блока, являются локальными. При каждом входе в блок им выделяется новая область памяти, а при выходе из блока память освобождается и значение переменной, следовательно, теряется. Локальные объекты видны с момента их описания до конца блока.
Если при выходе из блока нужно сохранить значение переменной, то ее следует описать как статическую в данном блоке, например:
{
static int a;
...
}
Теперь при выходе из блока переменная а будет сохранять в памяти свое место и значение и будет «видна» только в этом блоке, а также в блоках вложенных в него.
Глобальные объекты видны с момента их описания до конца файла всем функциям (блокам).
Если имя локального объекта (переменной) совпадает с именем глобального объекта, то локальный объект маскирует глобальный в этом блоке.
Область действия меток - функция, в которой метка используется.
Пример 9.1: Область видимости переменных.
#include <stdio. h>
int k = 1; /* k = 1 */
void main(void)
{
printf(“k = %d \n”, k); // Результат: k = 1
{
//1-й вложенный блок
int k = 2, m = 5;
printf(“ k = %d \t m = %d \n”, k, m); // Результат: k =2, m = 5
{
//2-й вложенный блок
int k=0;
printf(“ k = %d \t m = %d \n”, k, m); // Результат: k=0, m=5
}
printf(“ k = %d \n”, k); // Результат: k=2
}
printf(“ k = %d \n”, k); // Результат:k=1
}
В приведенной программе четыре уровня видимости:
1) внешний уровень;
2) тело функции main();
3) 1-й вложенный блок;
4) 2-й вложенный блок
Внешний уровень – уровень файла. Объект внешнего уровня виден “всем в файле”. Время его жизни – глобальное. На локальном уровне внешний объект может быть переопределен.
Различают два класса объектов: глобальные и локальные, но используют четыре описателя классов: extern, static, auto и register. Назначение и применение их приведено в табл. 9.1
Таблица 9.1
Классы памяти
Класс памяти
Ключевое слово
Время действия
Область действия
1. Автоматический
auto
временно
локальная
2. Регистровый
register
временно
локальная
3. Статический
static
постоянно
локальная
4. Внешний
extern
постоянно
глобальная (все файлы)
5.Внешний статический
static
постоянно
глобальная (один файл)
Классы 1, 2 и 3 описываются внутри функций; Классы 4 и 5 описываются вне функций.
По умолчанию (без спецификаторов класса памяти) переменные, описанные внутри функции, являются автоматическими. Переменные, описанные вне функций по умолчанию - глобальные.
Класс памяти extern указывает, что глобальная переменная описана где-то в другом месте (в этом или другом файле). Таким образом, описатель extern позволяет только сослаться на описание, сделанное в другом месте.
Для того, чтобы описание extern было корректным, необходимо чтобы описание самой переменной существовало только один раз в любом из файлов, образующих текст программы, например:
Файл 1 файл 2 файл 3
… … …
float p=0.33; extern float p; extern float p;
Замечание: Описание вида: extern тип имя_ переменной = значение; недопустимо.
Если переменная внутри функции имеет класс памяти extern, то она может быть описана только после описания этой функции, например:
Неправильно Правильно
… …
main () main ()
{ {
… …
} }
6 func_1() func_1()
{ {
k = k+2 extern int k;
} k = k+2;
int k = 10 }
… int k =10;
…
Ошибка состоит в том, что область видимости переменной начинается с точки ее описания.
Глобальные переменные могут быть инициализированы:
явно: int k = 10; static float a = 20.52;
неявно int k; static float a;
При неявной инициализации переменным присваиваются нулевые значения.
Класс памяти static на внешнем уровне маскирует описание переменных внутри файла, в котором они описаны. Другим файлам они не недоступны.
Класс памяти register предполагает хранение переменной во внутреннем регистре процессора. Время жизни и область видимости регистровой переменной такая же, как и у автоматической. Если компилятор не может разместить эту переменную в регистре, то она трактуется как автоматическая. По умолчанию значение регистровых переменных не определено.
К регистровой переменной нельзя применить операцию & - определение адреса. Их нельзя описывать на внешнем уровне.
Инициализация переменных определяется конструкцией вида:
Переменная = <инициализатор>;
Основные правила:
1. Если описание переменной начинается со служебного слова EXTERN, то явная инициализация не допустима.
2. Переменные, описанные на внешнем уровне можно явно инициализировать. При неявной инициализации они получают нулевые значения.
3. Cтатическим переменным можно присваивать значения константного выражения. По умолчанию они инициализируются нулем.
4. Автоматические и регистровые переменные инициализируются при
каждом входе в блок, где они описаны. По умолчанию их значения не определены.
5. Данные составного типа (массивы, структуры) можно инициализировать на внешнем или на внутреннем уровне. В последнем случае они инициализируются при входе в блок. При выходе из блока значения автоматических и регистровых переменных теряются.
Инициализация переменных составного типа.
Cинтаксис:
Имя переменной ={<cписок инициализаторов>};
Основные правила:
1.Значения константных выражений из списка инициализаторов присваиваются элементам объекта в порядке их следования.
2.Если в списке инициализаторов меньше элементов, чем в объекте составного типа, то оставшиеся элементы неявно инициализируются 0, а если больше, то регистрируется ошибка.
Пример:
int array [3] [3]=
{
{1,1,1}, //инициализация первой строки
{2,2,2}, //инициализация второй строки
{3,3,3}, // инициализация третьей строки
}
Внутренние скобки играют важную роль, особенно в тех случаях, когда имеются сложные описания, например, массивы структур. Здесь нужно быть особенно внимательным.
Пример неверной инициализации:
struct
{ int a1,a2,a3,;} st [2] [3]=
{
{1,2,3}, {4,5,6}, {7,8,9},
{10,11,12}, {13,14,15}, {16,17,18}
};
Примеры строковых инициализаторов
char str[] =”abc”;
char str[3] =”abcd” //неверно
char *str =”abcd”;
Указатель str будет инициализирован адресом массива типа char, содержащего символы ‘a’,’b’,’c’,’d’,’\0’.