Перейти к содержанию

Лекция 9: Области видимости переменных

Чак Норрис получил права, когда ему было 16 секунд.


Что такое память компьютера?

Память компьютера — это место, где хранятся данные и команды программ. Она состоит из ячеек, каждая из которых имеет уникальный адрес. Память работает по принципу записи и считывания данных с заданного адреса. Каждая ячейка памяти содержит определённое количество битов (обычно 1 байт = 8 бит), и данные в этих ячейках могут быть как инструкциями, так и значениями переменных программы.

Принципы работы памяти:

  1. Однородность — данные и команды программы хранятся в единой памяти.
  2. Адресность — доступ к данным осуществляется через адреса. Адрес переменной указывает на ячейку памяти, в которой она хранится.
  3. Иерархичность — память организована на разных уровнях, таких как кеш-память, оперативная память и дисковая память. Каждый уровень имеет свои характеристики по скорости и стоимости.

Пример:

int a = 10;  // Переменная 'a' занимает 4 байта (на 32-битной архитектуре)
printf("Адрес переменной a: %p", &a);  // Вывод адреса переменной

Области видимости переменных

Область видимости переменной — это часть программы, в которой переменная доступна для использования. В языке Си переменные могут быть локальными (внутри функций или блоков) или глобальными (доступными во всей программе).

Локальные переменные

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

Пример:

void func() {
    int x = 5;  // Локальная переменная
    printf("x = %d", x);  // Доступно только здесь
}
// printf("x = %d", x);  // Ошибка: x недоступно вне func

Глобальные переменные

Глобальные переменные объявляются вне всех функций, обычно в начале программы, и доступны всем функциям, которые находятся в этом файле. Если они объявлены как extern в других файлах, они могут быть использованы и там.

Пример:

int global = 10;  // Глобальная переменная

void func() {
    printf("global = %d", global);  // Доступно в любой функции этого файла
}

Сравнение локальных и глобальных переменных

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

Классы памяти

Класс памяти определяет время жизни переменной (сколько времени она существует) и её область видимости. В языке Си существуют следующие классы памяти:

1. Автоматический класс (auto)

Переменные по умолчанию имеют класс auto. Они создаются при входе в блок кода и уничтожаются при выходе из него. Эти переменные хранятся в стеке.

Пример:

void func() {
    auto int x = 5;  // Локальная переменная, класс auto
    printf("x = %d", x);
}

Переменная x существует только в функции func, и память под неё освобождается, как только выполнение функции завершится.

2. Статический класс (static)

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

Пример:

void counter() {
    static int count = 0;  // Статическая переменная
    count++;
    printf("count = %d\n", count);
}

int main() {
    counter();  // Вывод: count = 1
    counter();  // Вывод: count = 2
    return 0;
}

Переменная count сохраняет своё значение между вызовами функции counter.

3. Регистровый класс (register)

Переменные с классом register могут быть размещены в регистрах процессора, что ускоряет доступ к ним. Такие переменные не имеют явного адреса и не могут быть использованы с оператором & (например, нельзя получить адрес).

Пример:

void func() {
    register int i;  // Переменная будет попытаться размещена в регистре
    for (i = 0; i < 10; i++) {
        printf("%d ", i);
    }
}

4. Внешний класс (extern)

Переменные с классом extern определяются в одном файле и могут быть использованы в других файлах программы. Они позволяют работать с глобальными переменными, которые определены в другом месте программы.

Пример:

file1.c:

int global = 100;  // Определение внешней переменной

file2.c:

extern int global;  // Объявление внешней переменной
printf("global = %d", global);

Переменная global доступна в любом файле программы после её объявления как extern.


Объявление и определение переменной

Объявление переменной сообщает компилятору о её существовании, но не выделяет для неё памяти. Определение переменной выделяет память и может (но не обязательно) инициализировать её значением.

Пример:

extern int x;  // Объявление переменной
int x = 10;    // Определение переменной
  • Объявление с помощью extern позволяет компилятору знать о существовании переменной.
  • Определение выделяет память для переменной и присваивает ей начальное значение.

Правила:

  • Глобальные переменные объявляются с использованием extern, а определяются в одном файле.
  • Статические переменные сохраняют своё значение между вызовами, но остаются доступными только в пределах своей области видимости.

Класс памяти для функций

Функции в языке Си могут быть объявлены и определены с классами памяти static и extern.

  • extern (по умолчанию): Функция видна в других файлах программы.
  • static: Функция доступна только в текущем файле.

Пример:

file1.c:

static void hidden() {
    printf("Эта функция доступна только в file1.c");
}

void visible() {
    printf("Эта функция доступна в других файлах");
}

file2.c:

extern void visible();  // Функция доступна в других файлах
visible();  // Работает
hidden();   // Ошибка: нельзя вызвать, так как она статична
  • Функция visible с extern доступна в других файлах.
  • Функция hidden с static доступна только в файле file1.c.

Заключение

  • Локальные переменные используются для хранения временных данных и существуют только в пределах блока или функции, в которых они объявлены.
  • Глобальные переменные полезны для данных, которые должны быть доступны нескольким функциям в разных частях программы.
  • Использование static помогает ограничить область видимости переменной или функции, а использование register ускоряет выполнение.
  • Используйте extern, чтобы объявить переменные или функции, которые будут использоваться в разных файлах программы.

Я устал говорить: «Завтра будет все намного проще»
Я устал видеть каждый день одно и то же
По ночам мне одно и то же снится
Я устал видеть каждый день одни и те же лица