А. Богатырев - Руководство полного идиота по программированию (на языке Си), страница 6
Описание файла
PDF-файл из архива "А. Богатырев - Руководство полного идиота по программированию (на языке Си)", который расположен в категории "". Всё это находится в предмете "информатика" из 1 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Просмотр PDF-файла онлайн
Текст 6 страницы из PDF
Руководство полного идиота<br> по программированию (на языке Си)Чтобы вызывать их с разными аргументами!int res1, res2;...res1 = func(12 * x * x + 177, 865,'x');res2 = func(432 * y + x, 123 * y - 12, 'z');Кстати, вы заметили, что список фактических параметровследует через запятую;и выражений ровно столько, сколько параметров у функции?Функция описывает ПОСЛЕДОВАТЕЬНОСТЬ ДЕЙСТВИЙ,которую можно выполнить много раз,но с разными исходными данными (аргументами).В зависимости от данных она будет выдавать разные результаты,но выполняя одни и те же действия.В том то и состоит ее прелесть:мы не дублируем один кусок программы много раз,а просто "вызываем" его.Функция - абстракция АЛГОРИТМА, то есть последовательности действий.Ее конкретизация - вызов функции с уже определенными параметрами.------------------------------------------------------------------------Оператор return может находиться не только в конце функции,но и в ее середине.Он вызывает немедленное прекращение тела функции и возврат значенияв точку вызова.int f(int x){int y;y = x + 4;if(y > 10) return (x - 1);y *= 2;return (x + y);}РЕКУРСИВНЫЕ ФУНКЦИИ.
СТЕКРекурсивной называется функция, вызывающая сама себя.int factorial(int arg){if(arg == 1)return 1;elsereturn arg * factorial(arg - 1);}/* a *//* b */Эта функция при вызове factorial(n) вычислит произведениеn * (n-1) * ... * 3 * 2 * 1называемое "факториал числа n".В данной функции переменная arg будет отведена (а после и уничтожена) МНОГО РАЗ.Так что переменная factorial::arg должна получить еще и НОМЕР вызова функции:file:///Volumes/WININSTALL/assets/materials_informatiks.html38/6803.06.2015Андрей Богатырев.
Руководство полного идиота<br> по программированию (на языке Си)factorial::arg[уровень_вызова]И на каждом новом уровне новая переменная скрывает все предыдущие.Так для factorial(4) будет+----------------------------------------+|| factorial::arg[ 0_ой_раз ] есть 4||| factorial::arg[ 1_ый_раз ] есть 3||| factorial::arg[ 2_ой_раз ] есть 2||| factorial::arg[ 3_ий_раз ] есть 1|V --------++--------Затем пойдут возвраты из функций:+----------------------------------------+A| /* b */ return 4 * 6 = 24||| /* b */ return 3 * 2 = 6||| /* b */ return 2 * 1 = 2||| /* a */ return 1;|| --------++--------Такая конструкция называется СТЕК (stack).--------++-----------||+---------------+пустой стекПоложим в стек значение a|--------+|+-----------|V|+---------------+|a| <--- вершина стека+---------------+Положим в стек значение b--------++-----------||+---------------+|b| <--- вершина стека+---------------+|a|+---------------+Положим в стек значение c--------++-----------||+---------------+|c| <--- вершина стека+---------------+|b|+---------------+|a|+---------------+Аналогично, значения "снимаются со стека" в обратном порядке: c, b, a.В каждый момент времени у стека доступно для чтения (копирования) иливыбрасывания только данное, находящееся на ВЕРШИНЕ стека.file:///Volumes/WININSTALL/assets/materials_informatiks.html39/6803.06.2015Андрей Богатырев.
Руководство полного идиота<br> по программированию (на языке Си)Так и в нашей рекурсивной функции переменная factorial::argведет себя именно как стек (этот ящик-стек имеет имя arg) она имеет ОДНО имя, но разные значения в разных случаях.Переменные, которые АВТОМАТИЧЕСКИ ведут себя как стек,встречаются только в (рекурсивных) функциях.Стек - это часто встречающаяся в программировании конструкция.Если подобное поведение нужно программисту, он должен промоделироватьстек при помощи массива:int stack[10];int in_stack = 0;/* сколько элементов в стеке *//* Занесение значения в стек */void push(int x){stack[in_stack] = x;in_stack++;}/* Снять значение со стека */int pop(){if(in_stack == 0){printf("Стек пуст, ошибка.\n");return (-1);}in_stack--;return stack[in_stack];}Обратите в нимание, что нет нужды СТИРАТЬ (например обнулять)значения элементов массива, выкинутых с вершины стека.Они просто больше не используются, либо затираются новым значением припомещении на стек нового значения.void main(){push(1);push(2);push(3);while(in_stack > 0){printf("top=%d\n", pop());}}СТЕК И ФУНКЦИИБудем рассматривать каждый ВЫЗОВ функции как помещение в специальный стекбольшого "блока информации", включающего в частностиАРГУМЕНТЫ И ЛОКАЛЬНЫЕ ПЕРЕМЕННЫЕ вызываемой функции.Оператор return из вызванной функции выталкивает со стека ВЕСЬ такой блок.В качестве примера рассмотрим такую программу:int x = 7;int v = 333;/* глобальная *//* глобальная */int factorial(int n){int w; /* лишняя переменная, только для демонстрационных целей */file:///Volumes/WININSTALL/assets/materials_informatiks.html40/6803.06.2015Андрей Богатырев.
Руководство полного идиота<br> по программированию (на языке Си)w = n;if(n == 1)else}void func(){int x;return 1;return n * factorial(n-1);/* #a *//* #b *//* func::x */x = 777;/* #c */printf("Вызвана функция func()\n");}void main(){int y = 12;int z;/* main::y */z = factorial(3);printf("z=%d\n", z);/* A *//* B */func();/* C */}------------------------------------------------------------------------Выполнение программы начнется с вызова функции main().В точке /* A */|Vв з г л я д|V--------++-------|=======================||z = мусор||y = 12|+------+---------+|main()||x = 7 | v = 333 |+-----------------------+-----------+------+---------+----СТЕК ВЫЗОВОВ ФУНКЦИЙГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕВ каждый данный момент видимы переменные, которые находятсяa) на вершине (и только) стека вызовов функций.b) и незаслоненные ими глобальные переменные.В данном случае мы смотрим "сверху" и видим:main::z, main::y, ::x, ::v------------------------------------------------------------------------В точке /* B */ мы вызываем factorial(3).--------++-------|=======================||w = мусор||n=3||factorial(3)||=======================||+-|---> текущий оператор z = factorial(3);|z = мусор||y = 12|+------+---------+|main()||x = 7 | v = 333 |+-----------------------+-----------+------+---------+----СТЕК ВЫЗОВОВ ФУНКЦИЙГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕПри новом взгляде видимы:factorial(3)::w, factorial(3)::n, ::x, ::vСтали невидимы:file:///Volumes/WININSTALL/assets/materials_informatiks.html41/6803.06.2015Андрей Богатырев.
Руководство полного идиота<br> по программированию (на языке Си)main::z, main::yСтрока "текущий оператор ..." указывает место, с которого надо возобновитьвыполнение функции, когда мы вернемся в нее.------------------------------------------------------------------------Когда выполнение программы в функции factorial(3) дойдет до точки/* #b */ будет выполняться return 3 * factorial(2).В итоге будет вызвана функция factorial(2).--------++-------|=======================||w = мусор||n=2||factorial(2)||=======================||+-|---> текущий оператор return 3 * factorial(2);|w=3||n=3||factorial(3)||=======================||+-|---> текущий оператор z = factorial(3);|z = мусор||y = 12|+------+---------+|main()||x = 7 | v = 333 |+-----------------------+-----------+------+---------+----СТЕК ВЫЗОВОВ ФУНКЦИЙГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ------------------------------------------------------------------------Когда выполнение программы в функции factorial(2) дойдет до точки/* #b */ будет выполняться return 2 * factorial(1).В итоге будет вызвана функция factorial(1).--------++-------|=======================||w = мусор||n=1||factorial(1)||=======================||+-|---> текущий оператор return 2 * factorial(1);|w=2||n=2||factorial(2)||=======================||+-|---> текущий оператор return 3 * factorial(2);|w=3||n=3||factorial(3)||=======================||+-|---> текущий оператор z = factorial(3);|z = мусор||y = 12|+------+---------+|main()||x = 7 | v = 333 |+-----------------------+-----------+------+---------+----СТЕК ВЫЗОВОВ ФУНКЦИЙГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ------------------------------------------------------------------------Затем в factorial(1) выполнение программы дойдет до точки /* #a */и будет производиться return 1.При return вычеркивается ОДИН блок информации со стека вызовов функций,и возобновляется выполнение "текущего оператора" в функции,ставшей НОВОЙ вершиной стека вызовов.Заметьте, что в данной ситуации вызванные функции factorial(m) еще не завершились.file:///Volumes/WININSTALL/assets/materials_informatiks.html42/6803.06.2015Андрей Богатырев.
Руководство полного идиота<br> по программированию (на языке Си)В них еще ЕСТЬ что сделать: вычислить выражение в return,и собственно выполнить сам return. Вычисления будут продолжены.--------++-------|=======================||+-|---> текущий оператор return 1;|w=1||n=1||factorial(1)||=======================||+-|---> текущий оператор return 2 * factorial(1);|w=2||n=2||factorial(2)||=======================||+-|---> текущий оператор return 3 * factorial(2);|w=3||n=3||factorial(3)||=======================||+-|---> текущий оператор z = factorial(3);|z = мусор||y = 12|+------+---------+|main()||x = 7 | v = 333 |+-----------------------+-----------+------+---------+----СТЕК ВЫЗОВОВ ФУНКЦИЙГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ------------------------------------------------------------------------Начинается выталкивание функций со стека и выполнение операторов return;--------++-------|=======================||+-|---> текущий оператор return 2 * 1;|w=2||n=2||factorial(2)||=======================||+-|---> текущий оператор return 3 * factorial(2);|w=3||n=3||factorial(3)||=======================||+-|---> текущий оператор z = factorial(3);|z = мусор||y = 12|+------+---------+|main()||x = 7 | v = 333 |+-----------------------+-----------+------+---------+----СТЕК ВЫЗОВОВ ФУНКЦИЙГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ--------++-------|=======================||+-|---> текущий оператор return 3 * 2;|w=3||n=3||factorial(3)||=======================||+-|---> текущий оператор z = factorial(3);|z = мусор||y = 12|+------+---------+|main()||x = 7 | v = 333 |+-----------------------+-----------+------+---------+----СТЕК ВЫЗОВОВ ФУНКЦИЙГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕfile:///Volumes/WININSTALL/assets/materials_informatiks.html43/6803.06.2015Андрей Богатырев.