Delphi 7. Занятие 2_6. Часть 1.
Рассматриваемые вопросы первой части.
- Локализация данных через параметры.
- Соблюдение порядка параметров.
- Принципы передачи данных через параметры.
- Параметры-значения.
- Параметры-константы
- Статическая и динамическая память.
Содержание
Объявление процедур и функций.
Объявить процедуру или функцию — значит указать её заголовок, описать локальные переменные, написать код, который будет выполняться в процедуре.
Заголовок процедуры состоит из ключевого слова procedure. За ним следует имя процедуры, задаваемое программистом. Оно может состоять из латинских букв, цифр и знака подчёркивания. Имя должно начинаться с буквы.
После имени в круглых скобках указывается список формальных параметров.
В delphi параметры процедуры или функции могут формироваться несколькими способами.
Объявления параметров
Объявления параметров имеют следующий вид:
имя_параметра:тип_параметра;.
Если имён одного типа указывается несколько, то они перечисляются через запятую. Например:
procedure MyProc(i,j,k:integer; s:string; d1,d2:double; b:boolean)
var x,y:array[1..10] jf integer; //локальные переменные
begin
тело процедуры
End;
Локализация данных через параметры.
Процедуры и функции являются закрытыми системами. Всё, происходящее внутри процедуры, скрыто от взгляда остальной части программы. Но «вещь в себе» никому не нужна.
У процедур и функций есть «двери» и «форточки», через которые они общаются с «внешним миром».
Во первых, это список параметров. Через параметры в процедуры и функции передаются данные для обработки.
В некоторых случаях через параметры обратно в программу могут возвращаться вычисленные значения.
Во вторых, это глобальные переменные. Вспомним, что глобальные переменные, объявленные в секции implementation, видны во всех процедурах и функциях в этой секции.
Замечание. Такие переменные «глобальны» в пределах данного модуля. Они не видны из других модулей. Поэтому можно в разных модулях объявлять в этих секциях переменные с одинаковыми именами — программа их не перепутает.
Переменные, объявленные в секции interface, также глобальны для данного модуля.
Но они становятся глобальными для всей программы и могут быть видны из любого модуля, подключенного к данному, в многомодульной программе.
Поэтому имена переменных, объявленных в секции interface, необходимо делать уникальными в пределах программы.
Иначе возникает ошибка «конфликт имён».
В третьих, это обмен данными программы с процедурой по ссылке, о чём речь пойдёт ниже.
Кроме того, функция может возвращать одно значение указанного типа.
delphi передача параметров
Обмен данными через параметры-значения.
Механизм реализации вызова процедуры основан на использовании особым образом организованной области памяти — стеке. Объём стека составляет 64 Кб, что, естественно, накладывает некоторые ограничения на объём передаваемых в процедуру данных.
Стек — это очередь из значений.
Принцип его работы напоминает процесс заполнения автоматного магазина патронами. При этом последний вставленный патрон извлекается первым.
То есть принцип организации выборки из стека — последним вошёл, первым вышел.
Когда осуществляется вызов процедуры delphi, последовательно просматриваются константы и переменные, указанные в шапке (скобках) оператора вызова процедуры.
При этом их значения переписываются в стек.
Аналогично передаются параметры функции delphi при её вызове.
Размещение переменных в памяти.
Принцип организации стека.
Стек организован таким образом, что записать в него может осуществляться только в его «вершину», то есть в первую свободную перед занятыми. ячейку
Таким образом, значения переменных и констант, передаваемых в процедуру, копируются в стек.
При начале работы процедуры эти значения забираются из стека в обратном порядке и подставляются на место переменных, указанных в её шапке.
Например, пусть объявлена процедура:
procedure MyProc(var1,var2:integer; var3:char);
Её вызов может выглядеть следующим образом (пусть х=20):
MyProc(12,х,’A’);
При этом в стек последовательно будут записаны число 12, 20 и код символа ‘A’.
После формирования стека начнётся выполнение процедуры. Последовательно из стека будут считаны значения и присвоены переменным var1=12, var2=20 и var3=65 (код символа латинской «А»)
В дальнейшем эти значения будут подставлены в выражения, содержащие переменные var1, var2 и var3.
Локальные переменные, объявленные в процедуре, также хранят свои значения в стеке.
После выхода из процедуры стек «разрушается» и подготавливается для работы со следующей процедурой.
Таким образом, механизм использования стека разъединяет вызывающую переменную и переменную, используемую в процедуре.
Так, если в процессе работы процедуры присвоить новое значение переменной var2, то изменится значение в стеке, но это никак не отразится на значении самой переменной «х».
Обмен данными через параметры-константы.
Параметры-константы отличаются от параметров-значений тем, что «переменная», объявленная таким образом, не может в процедуре изменить своего значения.
Компилятор не пропустит такую попытку.
Например, объявим:
procedure MyProc(const var1:integer; var2:integer; var3:char);
Если в теле процедуры написать: var1:=var2+10; то компилятор выдаст ошибку: «левой части не может быть присвоено».
В то время как оператор var2:=var1+10; является верным.
Обмен данными через параметры-переменные.
Изменим синтаксис объявления процедуры следующим образом:
procedure MyProc(var v1:integer; v2:integer; v3:char);
В объявлении переменных появилось новое ключевое слово «var».
Оно означает, что переменная v1 будет хранить не значение, а адрес переменной, которая указана в операторе вызова процедуры (переменной y).
Поэтому вызов MyProc(12,х,’A’); недопустим, так как на первом и втором месте обязаны стоять переменные и их адреса должны быть переданы в вызываемую процедуру.
Правильным будет вызов:
MyProc(y,х,’A’);
Таким образом, переменной v1 будет передан адрес переменной y.
Такая запись кардинально изменяет ситуацию. Теперь обе переменные: и v1 в процедуре, и y будут указывать на одну и ту же ячейку памяти (на один и тот же адрес). Следовательно, изменяя v1, мы тем самым изменяем y.
Таким образом можно возвращать результаты работы процедуры, формируя значения var-переменных.
В дальнейшем мы увидим, что в этом есть свои существенные преимущества, но это является и источником серьёзных потенциальных ошибок.
При использовании параметров-переменных необходимо быть очень внимательным, чтобы не модифицировать исходные значения!
Например, изменяя в процедуре значение v1, мы тем самым автоматически изменяем значение y. Пусть после выхода из процедуры y должен участвовать в вычислениях со своим первоначальным значением.
Так как оно было изменено в процедуре, то результат вычислений окажется не верным.
По поводу функции отметим, что она может возвращать значения не только через var-переменные.
Функция сама может выступать в роли переменной, возвращая значение.
Принципы размещения переменных в памяти.
В самом деле, если написать:
implementation
var xx:integer; //1
procedure MyProc(xx:integer;); //2
var y:integer;
begin
y:=xx+10;
тело процедуры
end;
А затем вызвать процедуру оператором:
MyProc(xx);
то xx в этом вызове и xx в теле процедуры — совершенно разные переменные, хотя имеют одинаковое обозначение.
Переменная xx в теле процедуры хранит свои значения в стеке, а глобальная переменная xx расположена в сегменте данных программы.
Заметим, что переменная xx будет глобальной переменной, если программа состоит из одного модуля Unit1, то есть она «глобальная» в пределах модуля.
В противном случае, если модулей несколько, она будет «глобальной» в пределах модуля, но локальной по отношению ко всей программе (из других модулей она видна не будет.)
Истинно глобальные переменные объявляются в секции interface, откуда они видны всем модулям программы.
Значение глобальной переменной будет храниться в сегменте данных программы (участок памяти, выделяемой при компиляции программы для хранения значений переменных).
Окончательное создании программы — это сборка откомпилированных частей программы в файл .exe.
Такая сборка производится с помощьюпрограммы-компоновщика link.
В интегрированной среде разработки и программа-компилятор, и программа компоновщик встроены в IDE, составляя единый комплекс.
В принципе, можно писать части программы (модули) в любом текстовом редакторе.
Затем использовать отдельный компилятор для перевода текста этих модулей в машинные коды.
И, наконец, использовать программу-компоновщик, «собирающую» из файлов, полученных при компиляции и файлов сторонних библиотек единое целое — файл «.ехе» .
Ещё раз о сегментах программы.
Как следует из вышесказанного, при сборке программы под её размещение отводится участок памяти, состоящий из частей (сегментов).
Первый сегмент — сегмент кода программы. В нём содержатся команды, которые будут выполняться в процессе работы программы.
Второй сегмент — сегмент данных. Это участок памяти, отведённый для хранения значений переменных.
Третий сегмент отводится под организацию стека.
При загрузке программы на выполнение происходит её размещение в ОП (оперативную память). При этом производится статическое (не меняющееся в процессе работы программы!) распределения памяти под сегменты программы.
При динамическом объявлении (создании) переменных распределение памяти осуществляется непосредственно в процессе работы программы. Отводимая под переменные память выделяется и освобождается по мере надобности в соответствие с логикой программы.
Память для динамических переменных отводится в свободном от программ пространстве — «куче».
Операционная система следит за состоянием кучи и непосредственно участвует в процессе распределения памяти.
Далее:
- delphi передача в процедуру записей.
- delphi передача в процедуру массива.
- delphi открытый массив.
Продолжение Delphi 7. Занятие 2_6. Часть 2.