Сравнение старого и нового стилей объявления параметров функций



 

Если вам приходилось когда‑либо разбираться в старом С‑коде, то вы, возможно, обратили внимание на необычное (с точки зрения современного программиста) объявление параметров функции. Этот старый стиль объявления параметров, который иногда называют классическим форматом , устарел, но его до сих пор можно встретить в программах "эпохи раннего С". В C++ (и обновленном С‑коде) используется новая форма объявлений параметров функций. Но если вам придется работать со старыми С‑программами и, особенно, если понадобится переводить их в С++‑код, то вам будет полезно понимать и форму объявления параметров, "выдержанную" в старом стиле.

Объявление параметра функции согласно старому стилю состоит из двух частей: списка параметров, заключенного в круглые скобки, который приводится после имени функции, и собственно объявления параметров, которое должно находиться между закрывающей круглой скобкой и открывающей фигурной скобкой функции. Например, это "новое" объявление (т.е. по новому стилю)

 

 

будет выглядеть с использованием старого стиля несколько по‑другому.

 

 

Обратите внимание на то, что в классической форме после указания имени типа в списке может находиться несколько параметров. В новой форме объявления это не допускается.

В общем случае, чтобы преобразовать объявление параметров из старого стиля в новый (С++‑стиль), достаточно внести объявление типов параметров в круглые скобки, следующие за именем функции. При этом помните, что каждый параметр должен быть объявлен отдельно, с собственным спецификатором типа.

 

Рекурсия

Рекурсивная функцияэто функция, которая вызывает сама себя.

Рекурсия – это последняя тема, которую мы рассмотрим в этой главе. Рекурсия, которую иногда называют циклическим определением , представляет собой процесс определения чего‑либо на собственной основе. В области программирования под рекурсией понимается процесс вызова функцией самой себя. Функцию, которая вызывает саму себя, называют рекурсивной .

Классическим примером рекурсии является вычисление факториала числа с помощью функции factr() . Факториал числа N представляет собой произведение всех целых чисел от 1 до N . Например, факториал числа 3 равен 1x2x3 , или 6 . Рекурсивный способ вычисления факториала числа демонстрируется в следующей программе. Для сравнения сюда же включен и его нерекурсивный (итеративный) эквивалент.

 

 

Нерекурсивная версия функции fact() довольно проста и не требует расширенных пояснений. В ней используется цикл, в котором организовано перемножение последовательных чисел, начиная с 1 и заканчивая числом, заданным в качестве параметра: на каждой итерации цикла текущее значение управляющей переменной цикла умножается на текущее значение произведения, полученное в результате выполнения предыдущей итерации цикла.

Рекурсивная функция factr() несколько сложнее. Если она вызывается с аргументом, равным 1 , то сразу возвращает значение 1 . В противном случае она возвращает произведение factr(n‑1) * n . Для вычисления этого выражения вызывается метод factr() с аргументом n‑1 . Этот процесс повторяется до тех пор, пока аргумент не станет равным 1 , после чего вызванные ранее методы начнут возвращать значения. Например, при вычислении факториала числа 2 первое обращение к методу factr() приведет ко второму обращению к тому же методу, но с аргументом, равным 1 . Второй вызов метода factr() возвратит значение 1 , которое будет умножено на 2 (исходное значение параметра n ). Возможно, вам будет интересно вставить в функцию factr() инструкции cout , чтобы показать уровень каждого вызова и промежуточные результаты.

Когда функция вызывает сама себя, в системном стеке выделяется память для новых локальных переменных и параметров, и код функции с самого начала выполняется с этими новыми переменными. Рекурсивный вызов не создает новой копии функции. Новыми являются только аргументы. При возвращении каждого рекурсивного вызова из стека извлекаются старые локальные переменные и параметры, и выполнение функции возобновляется с "внутренней" точки ее вызова. О рекурсивных функциях можно сказать, что они "выдвигаются" и "задвигаются".

Следует иметь в виду, что в большинстве случаев использование рекурсивных функций не дает значительного сокращения объема кода. Кроме того, рекурсивные версии многих процедур выполняются медленнее, чем их итеративные эквиваленты, из‑за дополнительных затрат системных ресурсов, связанных с многократными вызовами функций. Слишком большое количество рекурсивных обращений к функции может вызвать переполнение стека. Поскольку локальные переменные и параметры сохраняются в системном стеке и каждый новый вызов создает новую копию этих переменных, может настать момент, когда память стека будет исчерпана. В этом случае могут быть разрушены другие ("ни в чем не повинные") данные. Но если рекурсия построена корректно, об этом вряд ли стоит волноваться.

Основное достоинство рекурсии состоит в том, что некоторые типы алгоритмов рекурсивно реализуются проще, чем их итеративные эквиваленты. Например, алгоритм сортировки Quicksort довольно трудно реализовать итеративным способом. Кроме того, некоторые задачи (особенно те, которые связаны с искусственным интеллектом) просто созданы для рекурсивных решений. Наконец, у некоторых программистов процесс мышления организован так, что им проще думать рекурсивно, чем итеративно.

При написании рекурсивной функции необходимо включить в нее инструкцию проверки условия (например, if‑инструкцию), которая бы обеспечивала выход из функции без выполнения рекурсивного вызова. Если этого не сделать, то, вызвав однажды такую функцию, из нее уже нельзя будет вернуться. При работе с рекурсией это самый распространенный тип ошибки. Поэтому при разработке программ с рекурсивными функциями не стоит скупиться на инструкции cout , чтобы быть в курсе того, что происходит в конкретной функции, и иметь возможность прервать ее работу в случае обнаружения ошибки.

Рассмотрим еще один пример рекурсивной функции. Функция reverse() использует рекурсию для отображения своего строкового аргумента в обратном порядке.

 

 

Функция reverse() проверяет, не передан ли ей в качестве параметра указатель на нуль, которым завершается строка. Если нет, то функция reverse() вызывает саму себя с указателем на следующий символ в строке. Этот "закручивающийся" процесс повторяется до тех пор, пока той же функции не будет передан указатель на нуль. Когда, наконец, обнаружится символ конца строки, пойдет процесс "раскручивания", т.е. вызванные ранее функции начнут возвращать значения, и каждый возврат будет сопровождаться "довыполнением" метода, т.е. отображением символа s . В результате исходная строка посимвольно отобразится в обратном порядке.

Создание рекурсивных функций часто вызывает трудности у начинающих программистов. Но с приходом опыта использование рекурсии становится для многих обычной практикой.

 


Дата добавления: 2018-09-22; просмотров: 501; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!