Раздел 8. Функции и их аргументы
Функции являются основными программными единицами в языках C, C++. Из них как из кирпичиков складывается программа. В отличие от алголоподобных языков в программах на C, C++ не допускается вложенность функций. Любая программа на C должна содержать главную функцию с именем main, с которой начинается выполнение программы. Вызов всех остальных функций прямо или косвенно инициируется главной функцией.
Аргументы функций – один из основных способов обмена информацией между частями программы. Конечно, для этих целей можно использовать и другие средства – глобальные переменные, внешнюю память (файлы на дисках). Но обилие глобальных переменных заставляет очень тщательно согласовывать имена общих переменных, усиливает зависимость программных единиц друг от друга, налагает ограничения на выбор имен локальных переменных. Кроме того, общедоступность глобальных переменных может привести к несогласованному их изменению разными функциями. А использование внешней памяти в ряде случаев приводит к резкому замедлению работы программы.
В системах программирования на IBM-совместимых компьютерах используются два основных механизма передачи параметров – через стек и через машинные регистры. Первый способ наиболее распространен, т.к. он не ограничивает объем передаваемой информации. В качестве стека используется определенный участок оперативной памяти с фиксированным, но управляемым диапазоном адресов (размер стека можно регулировать при настройке компилятора). Специальный регистр "следит" за очередным доступным участком стека. По адресу, хранящемуся в этом регистре, можно положить нужную порцию данных в стек и одновременно продвинуть содержимое регистра стека. Для этой цели система машинных команд предусматривает специальную операцию PUSH (от англ. – протолкнуть). Вторая машинная операция POP (от англ. pop up – выскочить наверх) позволяет извлечь из стека очередную порцию данных с одновременной коррекцией регистра стека. Системная программа, обслуживающая стек, следит за тем, чтобы стек не переполнился при записи и не оказался пустым при извлечении данных. Иногда механизм работы стека сравнивают с магазином огнестрельного оружия – в нем, пуля, попавшая последней в рожок автомата, стреляет первой. Этим же объясняется технология обслуживания стека LIFO (Last Input – First Output, т.е. последним вошел – первым вышел). Зарядка магазина имитирует запись в стек, а процедура стрельбы напоминает извлечение данных из стека.
Второй механизм позволяет ускорить процедуру передачи параметров, помещая их в специально выделенные регистры процессора. Однако таких регистров мало, поэтому этот механизм применяют в тех случаях, когда количество передаваемых параметров не превышает трех. В системе программирования BCB для регистровой передачи параметров существует специальная конструкция быстрого вызова (fastcall).
Несмотря на то, что главную функцию никто не вызывает, у нее тоже могут быть параметры, передаваемые ей операционной системой при запуске из командной строки. В большинстве случаев таких параметров два:
int main(int argc, char* argv[])
или
int main(int argc, char **argv)
Предположим, что наша программа с именем nameprog.exe была запущена из командной строки со следующими аргументами:
>nameprog par1 par2 par3
Тогда первый аргумент главной функции argc будет равен 4 (имя программы входит в список параметров командной строки). Второй аргумент функции main представляет собой строковый массив, элементами которого являются отдельные компоненты командной строки:
argv[0] | argv[1] | argv[2] | argv[3] |
"nameprog" | "par1" | "par2" | "par3" |
Параметры командной строки позволяют упростить запуск программы, работа которой зависит от информации, набираемой вручную с клавиатуры. Вспомните довольно старую, но надежную программу архивации данных:
>pkzip –a –r arch.zip qq1.doc qq2.doc
Функция может возвращать значение, – результат своей работы, или выполнять некоторое другое действие, не связанное с возвратом результата. Например, функция clrscr() осуществляет очистку экрана, но не возвращает никакого значения. Если функция возвращает значение, то в ее заголовке перед именем функции должен быть указан тип возвращаемого значения:
double mid(double x,double y)
В теле функции, возвращающей значение, обязан присутствовать оператор return (от англ. – возврат), содержащий результат работы функции – ее значение:
double mid(double x, double y)
{ return (x+y)/2.; }
Если функция не возвращает значение, то в ее заголовке перед именем функции должен быть указан тип void. В этом случае в теле функции может встретиться оператор return без параметра. Но оператор return может и отсутствовать – выход из функции произойдет при достижении последней фигурной скобки:
void print_v(int *a,int n)
{ int j;
printf("\n");
for(j=0; j<n; j++)
printf("%8d",a[j]);
printf("\n");
}
Если перед именем функции не указан ни один из стандартных типов и отсутствует спецификатор void, то считается, что функция возвращает значение типа int.