Локальные, глобальные и статические переменные
Переменные, объявленные в теле функции, считаются локальными, принадлежащими только данной функции и недоступными для других функций. Поэтому написание функций – работа достаточно автономная, не требующая знакомства с текстами других функций. Это позволяет многим исполнителям вести параллельную разработку блоков больших программ.
К числу локальных переменных следует отнести и формальные параметры функции. Всем им оперативная память выделяется в момент вызова функции и возвращается системе после завершения работы функции. Если объявление локальной переменной совмещается с ее инициализацией, то инициализация происходит при каждом вызове функции. Этим языки C, C++ отличаются от языка Паскаль, где инициализация происходит только один раз в момент загрузки программы в память.
Если локальная переменная объявляется со спецификатором static, то такая переменная причисляется к категории статических. Память, выделенная для статических переменных, сохраняется за ними до окончания работы программы. Поэтому значения статических переменных сохраняются после выхода из функции, и при повторном обращении функция-владелец может воспользоваться предыдущим значением своей статической переменной. Для других функций значение этой статической переменной продолжает оставаться недоступным.
Переменные, объявленные за пределами всех функций, относятся к разряду глобальных. Для всех функций, расположенных в этом же программном файле, глобальные переменные общедоступны. Для "чужих" функций, находящихся в других программных файлах, такие глобальные переменные могут оказаться доступными при соблюдении двух условий:
· глобальная переменная объявлена без спецификатора static;
· в другом программном файле или в теле "чужой" функции эта переменная упомянута со спецификатором extern.
Глобальная переменная, объявленная со спецификатором static, доступна только для функций данного программного файла.
Возврат значения функции
Значение, которая функция возвращает вызывающей программе, указывается в операторе return. В теле функции, возвращающей значение, может находиться несколько операторов return.
8.11. "Левые" функции
В документации по системам программирования и в сообщениях об ошибках иногда можно встретить термины lvalue и rvalue. Они обозначают, соответственно, величины, которые могут находиться слева (lvalue = left value) от знака равенства в операторе присваивания или справа (rvalue = right value).
Как правило, функции, возвращающие значение, используются в правой части оператора присваивания. Однако функции в качестве своего значения могут возвращать указатели и ссылки. А по указателям и ссылкам возможна запись. Именно такие функции называют "левыми".
Приведем в качестве примера функцию, возвращающую ссылку на максимум из двух своих аргументов:
double& max(double &x, double &y)
{ return (x>y)? x : y; }
Ее обычное использование:
double r=max(a,b);
Использование с учетом "левизны":
double a=5,b=6;
max(a,b)=10; //эквивалентно b=10;
Аналогичный вариант, когда функция max возвращает указатель:
double* max(double *x, double *y)
{ return (*x>*y)?*x:*y; }
.........................
double a=5,b=6;
*max(&a,&b)=10; //эквивалентно b=10;
Левая функция, возвращающая ссылку на максимальный элемент массива:
int& Mmax(int a[],int n)
{ int im=0; //индекс максимального элемента
for(int j=1;j<n;j++) im=(a[im>a[j])? im : j;
return a[im];
}
Левая функция, возвращающая указатель на максимальный элемент массива:
int* Mmax(int a[],int n)
{ int im=0; //индекс максимального элемента
for(int j=1;j<n;j++) im=(a[im>a[j])? im : j;
return &a[im];
}
Для запрета левого использования функции, возвращающей указатель или ссылку, достаточно разместить в начале ее заголовка спецификатор const:
const double& max(double &x, double &y)
{ return (x>y)? x : y); }
Оформление и вызов программных единиц в системе Turbo C
Все программные единицы в Си носят название функций и разницу в оформлении настоящих подпрограмм и настоящих функций можно обнаружить по типу возвращаемого значения. Если в качестве указания типа использовано служебное слово void, то перед нами типичная подпрограмма (в терминах Си – функция, не возвращающая значение). Перед заголовком объявляемой функции всегда присутствует либо стандартный тип, либо описатель типа с последующей звездочкой. Последнее означает что функция возвращает указатель на данное соответствующего типа. В частности, допускается, что таковым указателем может быть "void *" В самом общем виде заголовок функции выглядит следующим образом:
void имя_функции([параметры])
или
тип имя_функции([параметры])
или
тип * имя_функции([параметры])
Тело функции, расположенное после заголовка, заключается в фигурные скобки. Если функция возвращает значение, то в ее теле должен присутствовать хотя бы один оператор return c указанием возвращаемого значения. Например:
int sign(int x)
{
/* Определение знака целого числа */
if(x<0) return -1;
if(x>0) return 1;
return 0;
}
В отличие от Бейсика и Паскаля функции Си, не имеющие параметров, всегда сопровождаются пустыми круглыми скобками. Например – clrscr().
Головная программа на Си представлена функцией main, которая может иметь до трех параметров, связанных с извлечением аргументов из командной строки. Чаще всего эта функция не имеет параметров и не возвращает значение:
void main(void)
К функции-подпрограмме в Си обращаются, указав ее имя со списком фактических параметров:
имя_функции(фактические параметры);
Функция, возвращающая значение, может использоваться как операнд в выражении соответствующего типа:
int qq;
......
qq=getch(); /*ожидание ввода кода символа с клавиатуры*/
Однако, в большинстве примеров предыдущих глав вы могли заметить, что к функции getch обращаются и как к обычной подпрограмме, игнорируя возвращаемое значение. Это правило распространяется на любые функции Си. Конечно не любая из них в этом варианте может оказаться полезной. Например, допустимо но нелепо встретить строку:
sin(0.5);
Системная функция в данном случае проработает, но эффекта от такого вызова никто не заметит. Разве что немного увеличится время работы программы. А в случае с функцией getch игнорирование результата позволяет зафиксировать момент, когда пользователь нажал какую-то клавишу.
Оформление и вызов программных единиц в системе Turbo Pascal
Как дань своему прототипу, –– Паскаль называет свои подпрограммы процедурами и начинает их описание со строки вида:
procedure имя_процедуры[(параметры)];
Тело процедуры на Паскале в точности повторяет структуру головной программы. В нем могут присутствовать разделы описания меток (label), констант (const), типов данных (type), переменных (var) и других процедур и функций, входящих в состав данной процедуры. Наличие вложенных процедур и функций отличает Паскаль и от Си, и от Бейсика. Собственно вычислительная часть тела процедуры начинается со служебного слова begin и заканчивается соответствующим end, после которого в отличие от головной программы следует не точка, а точка с запятой.
Фрагмент программы от заголовка процедуры до завершающей операторной скобки end принято называть блоком. Для Паскаля характерна возможность использования вложенных блоков и с каждым из них можно связать натуральное число, соответствующее уровню вложения. Так, например, в блок A могут быть вложены два последовательных блока первого уровня - блоки B и C. Если же блок C вложен в блок B, входящий, в свою очередь в блок A, то блок C уже имеет уровень два по отношению к блоку A. В теле любого блока могут содержаться обращения к вложенным блокам своего первого уровня или к последовательным блокам такого же уровня. Блок не имеет права напрямую обратиться к своим процедурам второго или более высокого уровня вложенности.
Если процедура A обращается к процедуре B, то описание процедуры B должно предшествовать описанию процедуры A. Однако, если программа включает рекурсивную цепочку из двух или более процедур, одна из них должна объявляться (но не описываться) первой с добавкой forward. Соответствующее объявление носит название опережающего (forward – впереди бегущий футболист, нападающий) :
procedure имя_процедуры[(параметры)]; forward;
Описание опережающей процедуры располагается после других процедур рекурсивной цепочки, но в ее заголовке список параметров может уже не указываться.
Для обращения к процедуре достаточно задать ее имя с последующим набором фактических параметров.
Описание функции отличается только тем, что ее заголовок начинается со служебного слова function и заканчивается указанием типа функции.
function имя_функции[(параметры)]:тип;
Тело функции отличается от тела подпрограммы только наличием оператора присваивания:
имя_функции:=выражение;
Досрочный выход из тела функции или тела процедуры осуществляется по оператору exit.
Головная программа на Паскале выступает в роли контейнера, содержащего все свои функции и процедуры. Первым оператором головной программы может быть не обязательная строка:
program имя_программы;
Собственно тело головной программы начинается с операторной скобки begin, расположенной вслед за последним объявлением, и завершается закрывающей операторной скобкой end с последующей точкой.
Оформление модулей на Паскале
В отличие от головной программы с ее первым необязательным оператором, модуль обязан начинаться со следующего заголовка:
Unit имя_модуля;
Если в качестве имени паскалевской программы может выступать любой идентификатор, в том числе и достаточно длинный, то именем модуля может быть только имя файла, допустимое в операционной системе MS-DOS. Это связано с тем, что при некоторых обстоятельствах (например, если пользователь вместо режима Compile выбрал режим Make или Build) система программирования Turbo Pascal вынуждена заново оттранслировать пользовательские модули. И тогда она пытается открыть дисковые файлы, имена которых совпадают с именами обновляемых модулей.
Второе отличие модуля от головной программы заключается в структуре модуля, содержащего от одной до трех частей, начинающихся служебными словами interface, implementation и begin. Интерфейсная часть, следующая за словом interface, состоит из описания типов данных, констант и переменных, а также заголовков функций и процедур, доступных любой программе, использующей данный модуль.
Вслед за служебным словом implementation начинается раздел описания всех процедур и функций, включенных в состав модуля. Среди них, естественно, должны присутствовать все процедуры и функции, упомянутые в интерфейсной части. Однако, кроме них в состав модуля могут входить любые процедуры и функции внутреннего пользования, доступные только внутри данного модуля. Об их существовании внешний пользователь не обязан знать. Если после завершения исполнительного раздела (implementation) встречается операторная скобка begin, то за ней следует так называемая инициирующая часть модуля. Эта часть содержит обычные операторы, выполняемые непосредственно перед стартом головной программы. Как и программа, модуль завершается операторной скобкой end с последующей точкой.
Любая из описанных выше частей в модуле может быть пустой. Например, модуль, содержащий только интерфейсную часть с описанием структур данных типа календарных дат, позволит не повторять эти описания в основной программе:
Unit Calendar
interface
type
Days=(Mon,Tue,Wed,Thu,Fri,Sat,Sun);
WorkingDays=Mon..Fri;
Months=(Jan,Feb,Mar,Apr,May,June,July,Aug,Sept,Oct,Nov,Decem);
Summer=June..Aug;
Autumn=Sep..Nov;
Spring=Mar..May;
DayNo=1..31;
YearNo=1900..2119;
Date=record
Day:DayNo;
Month:Months;
Year:YearNo;
end;
implementation
end.
Модуль, содержащий только инициирующую часть, может произвести какие-то первоначальные действия – открыть временный файл, проверить принтер и т.п.