Глобальные и локальные переменные в Delphi

 

В Delphi существуют два типа переменных: локальные и глобальные. Во всех ранее приведенных в этой книге примерах использовались глобальные переменные. Глобальные переменные — это переменные, объявленные за пределами функции или процедуры. Например, переменные, объявленные в главном файле проекта Delphi или в разделах интерфейса или реализации модуля, является глобальными.
 
Хочешь получить больше информации по Delphi в понятной и доступной форме? Надоело разбирать непонятный код? Я объясню и разжую все тебе до мельчайших деталей - полноценный видеокурс по Delphi закажи прямо сейчас:
 

ЗАКАЗАТЬ

 
Пример использования нескольких глобальных переменных показан в листинге 5.8А.
 
Листинг 5.8А. Глобальные переменные в файле проекта
 
Листинг 5.8Б. Глобальные переменные в модуле
 
Глобальные переменные создаются во время запуска приложения, существуют, пока оно выполняется, и уничтожаются по завершении его работы. Эти переменные не только доступны в течение всего времени работы приложения, но и являются общедоступными. 
 
Любая создаваемая процедура или функция может обращаться к общедоступным переменным. Единственное исключение из этого правила — глобальная переменная, объявленная в разделе реализации модуля. В этом случае переменная остается глобальной, но доступна только в том модуле, в котором она объявлена.
 
Компилятор автоматически инициализирует глобальные переменные как "пустые". Целочисленные значения устанавливаются равными О. строковые значения равными ' '. а булевские — равными False. Глобальные значения можно также инициализировать вручную при их объявлении:
var х: Integer = 101;
 
Локальные переменные — это переменные, которые объявлены в процедуре или
функции. Объявление локальной переменной выглядит подобно показанному ниже:
 
Локальные переменные существенно отличаются от глобальных. Локальные переменные существуют только в течение короткого периода времени. Они создаются при вызове процедуры или функции и уничтожаются немедленно по завершении ее выполнения. Локальные переменные могут использоваться только в процедуре или функции, в которой они объявлены. В отличие от глобальных переменных, локальные переменные не инициализируются компилятором автоматически, и они не могут быть инициализированы во время объявления. Их всегда нужно инициализировать вручную в теле процедуры до их использования, поскольку до инициализации они содержат случайные значения.
 
Если указать глобальную переменную в качестве счетчика цикла f or, компилятор выведет предупреждение.
 
Глобальные переменные никогда не следует использовать в качестве счетчика цикла for, поскольку это ведет к снижению скорости выполнения цикла. Если же в качестве счетчика цикла for применяется локальная переменная, компилятор получает возможность использовать для подсчета регистры процессора (наиболее быстродействующий вид памяти компьютера), что обеспечивает наивысшую производительность.

Наилучший механизм передачи определенных пользователем значений функциям и процедурам — список параметров. В процедурах можно использовать также глобальные переменные, но этого следует стараться избегать.
Delphi поддерживает несколько типов параметров: значения, переменные, константы и параметры типа out.
Объявление параметров-значений выглядит следующим образом:
procedure ИмяПроцедуры{ИмяПараметра:  ТипДанных);

При вызове процедуры и передаче ей значения как параметра-значения процедура получает только копию исходного значения.
Листинг 5.9. Пример использования параметра-значения
Взгляните на следующие строки кода: 
х := -100; MakeAbsolute(х); { значение х по-прежнему равно -100 }
 
При вызове процедуры MakeAbsolute она создает параметр Num и копирует в него значение из переменной х. Процедура изменяет значение параметра Num. но это изменение никогда не оказывает влияние на переменную х, поскольку в действительности процедуре не известно, что в качестве параметра была передана переменная. Поскольку параметры действует подобно локальным переменным, изменения, выполненные в параметрах, по завершении процедуры теряются.

Если требуется изменять значение переменной в теле процедуры, необходимо использовать параметры-переменные. Параметры-переменные объявляют с помощью зарезервированного слова var:
procedure ИмяПроцедуры(var ПараметрПеременная:   ТипДанных);
 
Параметры-значения передают по значению, а параметры-переменные — по ссылке. Различие между этими способами передачи состоит в том, что параметры-значения получают копии значений, а параметры-переменные — адреса переменных. При использовании параметров-переменных программа работает непосредственно с исходными значениями, а не с их копиями. Переменные можно передавать только как параметры-переменные. При попытке передачи постоянного значения в качестве параметра-переменной возникнет ошибка компиляции.
 
Листинг 5.10. Пример использования параметра-переменной
 
Параметры-константы аналогичны параметрам-значениям, но не позволяют изменять значение параметра в теле процедуры или функции. Параметры-константы объявляют с помощью зарезервированного слова const:
Параметры-константы особенно полезны при передаче процедуре или функции строковых значений. Строковые значения следует всегда передавать как параметры-переменные или параметры-константы. Если строковое значение требуется изменять, необходимо использовать параметр-переменную, если нет — параметр-константу.
Никогда не следует передавать строковые значения в виде параметров-значений, поскольку строки могут быть очень большими, и постоянное создание копий строк при каждом вызове процедуры будет приводить к снижению производительности приложения.
 
out-параметры во многом подобны параметрам-переменным, поскольку они также передаются по ссылке. Эти параметры используют для возврата значений из процедуры или функции вызывающему коду.
 
Подобно параметрам-переменным, out-параметры принимают только переменные.

Если процедуре или функции необходимо передать более одного параметра, их нужно разделять символами точки с запятой.
 
Если два или более параметра имеют один тип данных, их можно объявлять вместе, разделяя запятыми.
function Мах(Х,  Y: Integer):  Integer; begin
if X > Y then
Result := X else
Result   := Y;
end;
 
В Delphi используемые по умолчанию параметры — это параметры-значения или параметры-константы, которые имеют заданное по умолчанию значение. Например, рассмотрим следующую процедуру. 
 
Процедура WriteLnEx может выводить строковое значение столько раз, сколько определено переданным ей параметром ACount. То есть, если это значение требуется вывести дважды, в параметре ACout необходимо передавать 2:
WriteLnEx("Hello', 2);
WriteLnEx('Something else',  2) ;
 
Достаточно быстро постоянная передача 2 в параметре ACount начинает казаться нудным и бесполезным занятием. Именно в подобных случаях можно использовать заданные по умолчанию параметры. Объявляя используемое по умолчанию значение в объявлении процедуры или функции, можно избавить себя от необходимости передавать значение вызову процедуры.
 
Синтаксис задания используемого по умолчанию параметра представлен ниже:
procedure ИмяПроцедуры(Параметр:   ТипДанных = Значение);
Дополненная версия процедуры WriteLnEx, в которой параметр ACount объявлен в качестве заданного по умолчанию, показана в листинге 5.11.
Листинг 5.11. Пример параметра, заданного по умолчанию
При объявлении используемого по умолчанию параметра изменяется только способ вызова процедуры или функции. Обратите внимание, что теперь процедуру WriteLnEx можно вызывать, не передавая значение в параметре ACount.
WriteLnEx('Hello');
В данном случае компилятор использует значение по умолчанию, указанное в заголовке процедуры, и в действительности вызов процедуры WriteLnEx выглядит следующим образом:
WriteLnEx ('Hello',  2);
Чтобы процедуру или функцию можно было успешно использовать в коде, необходимо знать, какие параметры она принимает. Чтобы ознакомиться со списком параметров процедуры или функции, в окне Code Editor (Редактор кода) введите имя процедуры или функции, затем символ левой круглой скобки и нажмите сочетание клавиш <CTRL+ALT+Пробел>. Функция Code Insight (Анализатор кода) окна Code Editor (Редактор кода) выведет список параметров, как показано на рис. 5.6. Обратите внимание, что строка всплывающей подсказки содержит значение параметра, заданного по умолчанию, а весь параметр заключен в квадратные скобки.
 
Заданные по умолчанию параметры должны объявляться в конце списка параметров. Например, компиляция следующей процедуры будет невозможна, поскольку заданные по умолчанию параметры не могут предшествовать стандартным параметрам. Любые параметры, объявляемые после заданного по умолчанию параметра, также должны быть заданными по умолчанию.

Рекурсивная процедура — это процедура, которая может вызывать саму себя. В действительности все процедуры могут вызывать самих себя, но если вызов не реализован правильно, результатом станет программная ошибка (бесконечный цикл), а не рекурсивная процедура. Ниже приведен пример неправильно созданной рекурсивной процедуры:

Вызов процедуры Bug приводит к катастрофическим результатам, поскольку она будет продолжать вызывать саму себя до тех пор, пока не использует все доступные ресурсы, после чего выполнение приложения прекратится вследствие генерации исключения (рис. 5.7). При запуске приложения из IDE-среды Delphi процедура Bug вызовет сбой как приложения, так и всей IDE-среды. Отсюда вывод: никогда не создавайте ничего, что работает подобно процедуре Bug.
 
Правильно реализованная рекурсивная процедура должна обладать средством управления возможным количеством вызовов самой себя. Следующий пример процедуры иллюстрирует возможное применение целочисленного параметра для реализации простого обратного отсчета.
Результат выполнения процедуры CountDown показан на рис. 5.8.
 
Рекурсивная процедура CountDown рекурсивно вызывает саму себя до тех пор, пока значение параметра AStart остается большим 0.
Рис. 5.7. Результат бесконечной рекурсии процедуры 
 
 
Листинг 5.12. Простая рекурсивная процедура
 
Рис. 5.8. Результат выполнения процедуры CountDown 
 
 Наиболее известным примером рекурсии является функция, вычисляющая факториалы. Факториал — это произведение всех целых чисел в диапазоне от 1 до п. Например, факториал 4 равен 24 (1*2*3*4).
Листинг 5.13. Рекурсивная функция
Упреждающие объявления тесно связаны с правилом Delphi, в соответствии с которым любой элемент программы должен быть объявлен до того, как его можно будет использовать. Следующее приложение работает правильно, поскольку функция MyAbs объявлена раньше использующей ее процедуры.
 
Но если процедура MyWriteAbsolute будет объявлена раньше функции MyAbs, компиляция приложения станет невозможной, поскольку процедура окажется не в состоянии использовать функцию MyAbs (она ее просто не увидит). В больших модулях (фактически, в любом модуле) постоянное изменение порядка объявлений процедур — бессмысленное и бесполезное занятие.
 
Упреждающие объявления помогают соблюдать правило "объявление перед использованием". Упреждающие объявления в действительности представляют собой заголовки процедур или функций, за которыми следует директива forward. Упреждающие объявления всегда должны предшествовать всем другим объявлениям. В данном случае потребуется создать упреждающее объявление функции MyAbs.
 
При наличии упреждающего объявления функция будет пригодна к использованию, даже если в блоке она будет расположена ниже всех остальных процедур и функций.
 
Листинг 5.14. Упреждающее объявление
 
бычно каждая функция и процедура в модуле должна иметь уникальное имя. Однако перегрузка позволяет объявлять несколько процедур и функций с одним именем. Перегруженные процедуры и функции могут иметь одно имя, но их списки параметров должны различаться количеством параметров, их типом или и тем, и другим. Чтобы выполнить перегрузку процедуры или функции, ее необходимо пометить директивой overload.
 
Листинг 5.15. Перегруженные функции
 
При вызове перегруженной функции Delphi определяет вариант функции, которую нужно вызвать, по передаваемым параметрам. Если при вызове перегруженной функции или процедуры в окне Code Editor (Редактор кода) нажать комбинацию клавиш <СTRL+ALT+Пробел>, Delphi отобразит списки параметров всех перегрузок, как показано на рис. 5.9.
 
Рис. 5.9. Отображение перегруженных списков параметров средством Code Insight
 
 Директива inline, появившаяся в Delphi 2005, предоставляет дополнительное управление компиляцией процедур и функций. Эта директива позволяет повысить скорость выполнения небольших процедур и функций. Помеченные этой директивой (встраиваемые) функции работают быстрее, но увеличивают размер исполняемого файла.
 
Чтобы успешно использовать директиву inline, необходимо знать, что происходит при вызове процедуры или функции и какое влияние директива inline оказывает на код.
 
При вызове процедуры или функции она должна зарезервировать память для всех своих локальных переменных и параметров. Память для локальных переменных и параметров резервируется в стеке, и этот процесс называется созданием стекового фрейма.
 
Стек — это сегмент памяти, который приложение использует при работе с переменными. Стек автоматически выделяется приложением при его запуске. По умолчанию все приложения Delphi выделяют для стека 16 Кбайт памяти, а максимальный размер стека может быть равен 1 Мбайт. Хотя эти значения можно изменять, как показано на рис. 5.10. это следует делать, только имея на то очень веские причины.
 
Рис. 5.10. Параметры настройки размеров стека 

 
Стековый фрейм — это часть памяти стека, в которой временно хранятся параметры и локальные переменные процедуры. По завершении работы процедуры стековый фрейм, занятый параметрами и локальными переменными, автоматически освобождается. Именно поэтому локальные переменные и параметры нельзя использовать вне процедуры или функции, в которой они объявлены.
 
Все эти действия по резервированию и освобождению памяти выполняются отнюдь не по мановению волшебной палочки. Хотя программисту Delphi этот процесс может представляться "магическим", чтобы необходимые действия происходили во время выполнения, компилятор Delphi должен сгенерировать инструкции на машинном языке. Поэтому по завершении компиляции процедуры или функции исполняемый файл содержит не только машинную версию логики процедуры, но определенный код входа и выхода из процедуры, управляющий действиями по резервированию и освобождению области стека. Кроме того, для каждого вызова процедуры компилятор Delphi должен сгенерировать инструкции на машинном языке, выполняющие передачу параметров и собственно вызов процедуры.
 
Рассмотрим инструкции на машинном языке, генерируемые для очень простой функции MyAbs, которая возвращает абсолютное значение целого числа. Код Delphi этой функции показан в листинге 5.16.
Листинг 5.16. Встраиваемая функция
 
Для вызова функции MyAbs в главном блоке компилятор Delphi генерирует следующие три инструкции (оптимизация компилятора отключена): 
 
Первая инструкция помещает число -20 в регистр еах. Это необходимо, поскольку функция MyAbs ожидает, что значение параметра I должно храниться в регистре еах. Вторая инструкция явно вызывает функцию MyAbs, а последняя копирует результат ее выполнения в переменную х. Значение $0040565с — это адрес переменной х в памяти (обратите внимание, что это значение не является постоянным).
Для самой функции MyAbs компилятор генерирует следующие инструкции:
Первые две инструкции создают стековый фрейм. Третья инструкция (add esp, -$08) резервируют в стеке 8 байт памяти (достаточные для хранения двух целочисленных значений). Для резервирования области в стеке логичнее было бы использовать инструкцию sub esp, $08, но в данном случае разработчики компилятора Delphi прибегли к небольшому ухищрению.
 
Добавление отрицательного значения равносильно вычитанию положительного значения, но добавление выполняется быстрее. Четвертая инструкция помещает значение параметра I в стек.
 
Вторая часть сгенерированного кода выполняет проверку необходимости преобразования значения параметра I в положительное значение.
 
Первая инструкция сравнивает значение параметра I. помещенное в стек, с нулем. Вторая инструкция — это инструкция перехода по условию "не меньше", которая определяет необходимость преобразования значения параметра I в положительное значение. Если значение этого параметра больше 0, преобразование значения не требуется, и функция копирует исходное значение параметра в переменную Result функции:

 

dle

Помоги проекту! Расскажи друзьям об этом сайте: