Файл: ПОНЯТИЕ ПЕРЕМЕННОЙ В ПРОГРАММИРОВАНИИ. ВИДЫ И ТИПЫ ПЕРЕМЕННЫХ (Простые и сложные типы данных).pdf

ВУЗ: Не указан

Категория: Курсовая работа

Дисциплина: Не указана

Добавлен: 01.04.2023

Просмотров: 423

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

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

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

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

Указатели - это переменные, чьи значения являются адресами других переменных. Создавая указатель, программист указывает на местоположение памяти в куче, что задаёт начальное значение переменной и говорит программе, где получить доступ к этому значению. Из-за динамической природы кучи ЦП не принимает участия в контроле над ней; в языках без сборщика мусора (C, C++) разработчику нужно вручную освобождать участки памяти, которые больше не нужны. Если этого не делать, могут возникнуть утечки и фрагментация памяти, что существенно замедлит работу кучи.

Стек, в сравнении с кучей, работает быстрее. Связано это с тем, что в куче переменные разбросаны по памяти, а не доступны на верхушке стека. Некорректное управление памятью в куче приводит к замедлению её работы; тем не менее, эти недостатки не уменьшают важности кучи при разработке программ — если есть необходимость работать с динамическими или глобальными переменными, лучше воспользоваться кучей. [1]

1.4. Объявление переменных

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

В Си любая переменная должна быть объявлена раньше, чем она будет использована; обычно все переменные объявляются в начале функции перед первой исполняемой инструкцией. В объявлении описываются свойства переменных. [3]


Общий формат объявления переменных представлен на схеме ниже. В схеме сами элементы представлены в угловых скобках «<» и «>», в квадратных скобках обозначены необязательные для использования элементы.

[<спецификатор_класса_памяти>] [<модификатор(ы)>] <тип> <имя1>[=<инициализатор1>], <имя2>[=<инициализатор2>],…, <имяN>[=<инициализаторN>];

Элемент <спецификатор_класса_памяти> является необязательным и может принимать одно из доступных значений из ключевых слов:

  • auto;
  • register;
  • static;
  • extern.

Подробнее про них будет рассказано в параграфе «Классы памяти».

Элемент <модификатор(ы)> является необязательным.

Обычно переменной соответствует стандартная длина в соответствии с обозначенным типом, обусловленная аппаратными особенностями используемого вычислительного устройства. Например, для компьютеров IBM PC стандартная длина слова в памяти составляет 2/4 байта. Соответственно, тип переменной, к примеру, int определяет соответствующую интерпретацию - первый бит содержит разряд знака, а остальные 15/31 бит будут содержать значение числа.

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

Для модификаторов доступны следующие ключевые слова:

  • short;
  • long;
  • signed;
  • unsigned;
  • const;
  • volatile.

Модификаторы short и long должны были соответствовать длинам, отличным от стандартных длин для типов данных. Но компилятор языка Си не реагирует на модификатор short укорачиванием длины.

Модификатор long расширяет переменную int c двух до четырех байт.

Модификаторы signed и unsigned задают интерпретацию доступных для типа данных значений, как, соответственно, знаковую и беззнаковую. Данный модификатор возможно использовать только для типов int и char.

Для переменной можно указывать несколько модификаторов. Но при этом нужно помнить как об ограничениях с точки зрения компилятора, так и об ограничениях здравого смысла.

Например, нельзя использовать модификатор signed не для типов int и char. Также, невозможно объявить переменную одновременно знаковой и беззнаковой – «signed unsigned».

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

Следующие два модификатора – const и volatile – влияют не на доступный диапазон значений переменной, а на возможность изменения значений.


Mодификатор const сообщает компилятору о том, что значение переменной присваивается в самом начале, на этапе компиляции. Значение переменной устанавливается путем инициализации и никаким образом не должно изменяться во время выполнения операторами программы.

Модификатор volatile является полной противоположностью для модификатора const. Он сообщает компилятору о том, что значение, которое принимает переменная может быть изменено не только внутри самой программы, но также и каким-либо внешним воздействием со стороны операционной системы. Например таким как программа прерываний или порт ввода-вывода.

Элемент <тип> является обязательным и должен принимать один из допустимых типов данных.

При этом тип при описании переменной может быть не указан, если объявлены модификаторы. В таком случае компилятор присваивает переменной тип int, и применяет указанные модификаторы. Например, возможно такое объявление:

unsigned a;

Элементы вида <имяN> являются идентификатором переменной. Через них в дальнейшем будет выполняться взаимодействие с переменной внутри программы.

Элементы вида [=<инициализаторN>] являются необязательными и могут выполнять изначальную инициализацию значений переменной. В качестве инициализатора может выступать любое допустимое значение, например число или строка, другая переменная, результат выполнения функции.

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

Ниже приведены несколько примеров объявления переменных с комментариями:

int a; // объявление целочисленной переменной

unsigned b; // объявление беззнаковой целочисленной переменной

unsigned int b; // аналогично предыдущей строке

char g = 'Q', k = 'm'; // объявление двух символьных переменных с инициализацией значений

const float pi=3.14155926; // объявление и инициализация константной вещественной переменной. В дальнейшем коде значение переменной pi не может быть изменено.

Если переменной не присвоено начальное значение, то в случае глобальной переменной её значение заполняется нулями, а для локальной переменной начальное значение будет неопределённым.[12] После объявления переменной, участок оперативной памяти, на который ссылается переменная, может хранить «мусорное» значение – некие случайные данные, которые остались в области памяти.


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

Переменные можно разделить на две группы по области видимости – на локальные и глобальные переменные.

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

Локальные переменные, напротив, ограничены только своей областью видимости. Например, областью видимости создающей переменную функции.

Например, рассмотрим следующий пример кода:

int globalVar = 1;

void foo() {

printf("foo: %d\n", globalVar);

}

void bar(int globalVar) {

printf("bar: %d\n", globalVar);

}

int main() {

foo();

bar(2);

getch();

}

В результате выполнения данной программы будет выведено

foo: 1

bar: 2

Почему так происходит? В самом начале объявляется глобальная переменная globalVar, и инициализируется значением 1. Теперь данная переменная доступна из любого участка кода, и функция foo получает доступ к переменной globalVar, и выводит её значение, равное единице.

В функции bar глобальная переменная становится недоступна, так как внутри функции есть параметр с таким же именем, который перекрывает видимость глобальной переменной.

Рассмотрим другой пример, с локальной переменной.

int main() {

{

int y = 30;

}

printf("%d", y);

}

В данном примере объявлена локальная переменная y, и данный пример не скомпилируется, потому что локальная переменная существует только внутри своего блока, у не доступна в строке printf("%d", y) при выводе.

Видимость объявленных переменных в программе определяется следующими правилами:

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

1.6. Классы памяти переменных

Как было указано в параграфе объявления переменных, переменной при объявлении можно не только указать тип данных, модификатор, имя и начальное значение. Также для переменной можно указать спецификатор класса памяти. [4]

Рассмотрим доступные в языке Си классы памяти.


Автоматический класс памяти. Принимает значение auto. Является классом, который используется для переменных автоматически, если не указан другой класс. Такие переменные располагаются на стеке, а область видимости переменных ограничена своим блоком. Спецификатор auto уведомляет компилятор о том, что локальная переменная, перед именем которой он стоит, создаётся при входе в блок и разрушается при выходе из блока. Все переменные, определённые внутри функции, являются автоматическими по умолчанию, и поэтому ключевое слово auto используется крайне редко. Например, следующие объявления переменных дадут одинаковый результат:

int a;

auto int a;

Следующий рассматриваемый класс памяти – регистровый. Когда мы определяем регистровую переменную, то мы просим компилятор, чтобы переменная располагалась в регистре, а не в оперативной памяти. Компилятор может сделать переменную регистровой, если позволяют условия (регистры не заняты, и по мнению компилятора это не приведёт к увеличению издержек). Регистровые переменные определяются с помощью ключевого слово register перед типом:

register int x = 20;

register int y = 30;

Так как регистровая переменная не имеет адреса, то к ней не применима операция взятия адреса, это вызовет ошибку во время компиляции. Аргументы функции также могут быть заданы как register. Внутри функции они будут вести себя также, как и регистровые переменные.

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

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

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