Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Языки и методы программирования - лекция-7: выз...

Anton
December 08, 2024

Языки и методы программирования - лекция-7: вызов подпрограмм, передача параметров через стек на Си

Лекция курса "Языки и методы программирования"
Лекция-7: вызов подпрограмм, передача параметров через стек на Си
- Синтаксис объявления подпрограммы на Си
- Пример программы: механизм передачи параметров через стек и получение результата через регистр по шагам
- Рекурсия
- Рекурсия vs цикл
- Демонстрация отличия между рекурсией и циклом в работе с памятью на примере программ, вычисляющих факториал рекурсивно и в цикле
- Переполнение стека

Anton

December 08, 2024
Tweet

More Decks by Anton

Other Decks in Education

Transcript

  1. Функция • void — возвращать значение не нужно • (такую

    подпрограмму можно называть процедурой) void func_name(int param1, int param2) { // тело функции }
  2. Замечание • Подпрограмма, функция, процедура, метод (в ООП) — обычно

    взаимозаменяемые термины, вполне можно использовать как синонимы • Иногда более строго определяют функцию как подпрограмму, возвращающую значение, • а процедуру, как подпрограмму, не возвращающую значение • Метод — это функция, определенная для объекта (в объектно- ориентированном программировании) • На практике они легко превращаются одна в другую • За строгостью терминов конкретно вот этих не будем следить
  3. Пусть программа вычисляет формулу • res = [(p1 - p2)^2

    - (p3 + p4)^2]^2 • Выделим подпрограммы-функции: - square_diff — квадрат разности - square_sum — квадрат суммы - func1 — формула целиком - main — главная функция (ввод-вывод)
  4. #include <stdio.h> int square_diff(int a, int b) { return (a

    - b) * (a - b); } int square_sum(int a, int b) { return (a + b) * (a + b); } int func1(int p1, int p2, int p3, int p4) { int val1 = square_diff(p1, p2); int val2 = square_sum(p3, p4); int res = square_diff(val1, val2); return res; } int main() { int p1 = 8; int p2 = 6; int p3 = 3; int p4 = 5; int res = func1(p1, p2, p3, p4); printf("res=%d\n", res); }
  5. Замечания • Адреса текущей инструкции и возврата на картинках выше

    не являются адресами памяти, т. к. одна строчка на языке Си может быть развернута в несколько строчек на языке ассемблера • Волшебство восстановления адреса возврата $ra после возвращения из функции: на самом деле мы сохраняем регистр $ra на стеке в начале выполнения текущей функции и восстанавливаем его перед возвратом из функции • (это не отражено на картинках) • Так же как и некоторые другие регистры, которые могут быть использованы не только внутри текущей функции, но и в функции, которая текущую функцию вызвала
  6. Рекурсия • В программировании: функция, которая вычисляет собственное значение, вызывая

    сама себя • Вообще: самоповтор объекта внутри себя • «Чтобы понять рекурсию, нужно понять рекурсию»
  7. Шел по улице жучок В модном пиджачке. На груди блестел

    значок, А на том значке Нарисован был жучок, Тоже в пиджачке. И на нем висел значок, А на том значке, Был еще один жучок... Но он был так мал, Что глядел я целый час И не разобрал: Был ли у жучка значок? Был ли на значке жучок? Андрей Усачёв Жучок
  8. Рекурсия vs цикл • Факториал • n! = 1 *

    2 * 3 * … * n • 4! = 1 * 2 * 3 * 4 = 24
  9. Факториал: рекурсия #include <stdio.h> int fact_recur(int n) { if(n >

    1) { return n * fact_recur(n - 1); } else { return 1; } } int main() { int n = 4; int res = fact_recur(n); printf("recur: %d!=%d\n", n, res); }
  10. Факториал: цикл #include <stdio.h> int fact_loop(int n) { int res

    = 1; for(int i = 2; i <= n; i++) { res = res * i; } return res; } int main() { int n = 4; int res = fact_loop(n); printf("loop: %d!=%d\n", n, res); }
  11. Итого • Рекурсия для каждой итерации выделяет новый кадр на

    стеке • Цикл обходится одним кадром • При этом: кадр стека рекурсивной функции не меняется в процессе выполнения функции (в нашем случае) • Если отдельный подход в программировании, который базируется на использовании «не имеющих внутреннего состояния» функций
  12. Переполнение стека • Stack overflow • Хотим выделить еще немного

    места на стеке (например, при вызове функции), но • места в стеке больше не хватает
  13. Задания • Воспроизвести код лекции - простой вызов функции -

    вычисление факториала рекурсией и в цикле • Оформить участок кода в виде подпрограммы в одной из старых работ: - вычисление полинома разностным методом, - решение квадратного уравнения - или «игра: угадать сумму чисел» • Вызвать переполнение стека, показать результат
  14. Переполнение стека ] Ошибка сегментирования (сделан дамп памяти) int stack_overflow()

    { return stack_overflow(); } int main() { printf("stack overflow:%d\n", stack_overflow()); }