Область применения и срок службы переменных


Оглавление

  1. Схема памяти программы на языке C
    1. ОЗУ
    2. ПЗУ
    3. В чем разница между статически определенной переменной и динамически определенной переменной?
  2. Область действия переменной
    1. Область видимости блока
    2. Область видимости файла
    3. Область видимости программы
  3. В чем разница между определением и объявлением переменной?
  4. Классы хранения
    1. авто
    2. статический
    3. extern
    4. регистр

Схема памяти программы на языке C

Прежде чем перейти к изучению области видимости и времени жизни переменной в C, нам сначала нужно определить структуру памяти обычной программы.
Как мы знаем, существует два типа памяти RAM (Random Access Memory), которая используется для хранения данных, генерируемых программой, и ROM (Read Only Memory), которая используется для хранения инструкций программы.

RAM

+---------------------+
|    Memory Mapped    |
|    Peripherals      |
+---------------------+
|    Initialized      |
|    Data Segment     |
+---------------------+
|    Uninitialized    |
|    Data Segment     |
+-----+---------------+
|     | Heap          |
|     |               |
|     v               |
|                     |
|                     |
|                     |
|                     |
|     ^               |
|     |               |
|     | Stack         |
+-----+---------------+
Вход в полноэкранный режим Выход из полноэкранного режима
  • Оперативная память делится в основном на четыре части:
    1. Периферийные устройства, отображаемые в памяти
    2. Сегмент инициализированных данных: этот сегмент содержит все переменные, которые были инициализированы значением.
    3. Сегмент неинициализированных данных (bss)Содержит все неинициализированные переменные (те, которые были определены, но которым не присвоено значение).
    4. Куча
    5. Стек

ПЗУ

+---------------------+
| Interrupt Vector    |
| Table (IVT)         |
+---------------------+
| Text Segment        |
|                     |
+---------------------+
| Constant data       |
| Segment (.ro data)  |
+---------------------+
| Boot loader         |
|                     |
|                     |
+---------------------+
Вход в полноэкранный режим Выход из полноэкранного режима

Здесь мы имеем дело с текстовым сегментом, который используется для хранения инструкции (кода) программы, и сегментом данных константы, который используется для хранения каждой статически определенной константы.

В чем разница между статически определенной переменной и динамически определенной переменной?

Переменные, которые имеют значение до начала выполнения программы, называются статически определенными переменными, а переменные, которые не имеют значения до начала выполнения программы, но получают значение во время выполнения и получают его из другого места, называются динамически определенными переменными.

Область видимости переменной

Область видимости переменной — это область, в которой мы можем использовать переменную.
В языке Си существует три различных области видимости переменных:

  1. Область видимости блока (область видимости функции)
  2. Область видимости файла
  3. Область видимости программы

Область видимости блока

  • Переменные, имеющие такую область видимости, ограничены в своем использовании областью (блоком), в которой они были определены, мы не можем использовать их вне блока кода.
  • Блок (блок кода) — это вся область, начинающаяся с { и заканчивающаяся }.

Область видимости файла

  • Переменные, на которые мы можем ссылаться в любом месте файла, в котором они были объявлены.
  • Есть два класса хранения, которые мы рассматриваем в отношении области видимости файла, static и extern.

Программная область видимости

  • Это все нестатические глобальные переменные, то есть к ним можно обращаться из любой точки программы, но чтобы использовать глобальную переменную, которая была определена в файле (модуле), находящемся в другом месте от того, где мы собираемся ее использовать, мы должны сначала объявить ее.
  • Время жизни глобальной переменной — это все время жизни программы.

В чем разница между определением и объявлением переменной?

  • Объявление — это информирование компилятора о существовании переменной, чтобы компилятор мог игнорировать ее на данный момент, а позже компоновщик придет и свяжет значение с этой переменной, мы никогда не можем использовать переменную, которая не была сначала объявлена.
  • Объявление подразумевает указание переменной символа и типа данных. Например, ключевое слово extern int k просто сообщает компилятору, что есть переменная k и она имеет тип данных int, который был определен где-то в другом месте программы, используйте ее пока, а компоновщик обработает ее значение.
  • Только функции и константы могут быть только объявлены, а не определены и объявлены.
  • Определение предоставляет компилятору всю информацию, необходимую для генерации машинного кода, когда эта сущность используется позже в программе, (у компоновщика нет работы, когда компилятор может видеть определение переменной).
  • Поскольку постоянные переменные являются статическими переменными (т.е. они должны иметь значение до начала выполнения программы), определение должно быть предоставлено программистом до процесса компиляции.
  • Объявление встроенного типа, такого как int, автоматически является определением, потому что компилятор знает, сколько места для него выделить.
  • Примеры:
/* Delcare and define int variables i and j. */
int i = 0;
int j;

/* Decleration without definition. */
extern int k;

int main()
{
  /* This results in an error because there is no declaration instance for the variable l.
      error: ld returned 1 exit status */
  i = k + l;
}
Вход в полноэкранный режим Выход из полноэкранного режима

Классы хранения

  • Си предоставляет различные типы классов хранения, наиболее часто используемые из них:
  1. auto
  2. static
  3. extern
  4. register

Давайте возьмем все и узнаем, что они делают.

auto

  • Этот класс-спецификатор используется редко, поскольку он является классом хранения по умолчанию для переменных, скопированных в блок.
  • Каждый раз при входе в блок хранилище для переменных auto, определенных в этом блоке, становится доступным и перестает существовать, как только мы выходим из блока.
  • Выделение и деаллокация переменной в памяти происходит автоматически, отсюда и название.
  • Все неинициализированные автопеременные имеют мусорные значения, поэтому мы должны инициализировать их перед использованием.
  • Пример:
/* Both these variables are similar to each other. */
int x;
auto int x;
Вход в полноэкранный режим Выход из полноэкранного режима

static

  • Он оказывает два различных эффекта на переменную в зависимости от ее области применения — локальной или глобальной.
  • Если переменная локальная, то спецификатор static увеличивает время ее жизни на всю программу, но не расширяет ее область действия, ее область действия ограничена функцией, в которой она была определена.
  • Поскольку время жизни переменной было увеличено, ее место хранения изменилось с обычной локальной переменной, хранящейся на стеке, на постоянную переменную, хранящуюся в памяти программы.
  • Пример:
void myFunc(int i)
{
  int x = 2;
  static int result = 0;
  result += x;
  printf("The result is %d on the %d iteration.n", result, i);
}

int main(void)
{
  for (int i = 0; i < 4; i++)
  {
    myFunc(i);
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима
  • По сути, спецификатор static переносит значение переменной от одного вызова функции к другому.
  • При использовании в глобальной переменной спецификатор static ограничивает область действия этой переменной только тем файлом (модулем), в котором она была определена (т.е. другими словами, мы не можем использовать ключевое слово extern для таких переменных).
  • Спецификатор static также может быть использован для функции, ограничивая ее использование так же, как и для глобальных переменных.

extern

  • Используется для информирования компилятора (объявления) о том, что переменная с определенным именем и типом данных существует где-то в программе, и пока просто используйте ее как есть, а присвоением ей значения займется компоновщик.
  • По сути, это используется для расширения области видимости глобальной переменной.
  • Пример, если у нас есть два файла file1.c и file2.c:

  • file1.c

/*Decleration and defintion of a global variable x.*/
int x = 8;
Вход в полноэкранный режим Выйти из полноэкранного режима
#include "file1.c"

int main(void)
{
  extern int x;

  printf("The value of x %d.n", x);

  return 0;
}
Войти в полноэкранный режим Выход из полноэкранного режима
  • Обратите внимание, что включение C-файла в другой файл является законным, но нежелательным действием. В реальной программе мы скорее определим наши переменные в заголовочном файле, а затем включим его, этот пример приведен только для иллюстрации.

регистр

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

Оцените статью
Procodings.ru
Добавить комментарий