Г. Шилдт - Полный справочник по C++ (1109478), страница 30
Текст из файла (страница 30)
Рекурсивная версия функции двоек() несколько сложнее. Если функция двоек[» вызывается с аргументом 1, она возвращает число 1. В противном случае она возврашает значение васек(п-1)*п. Чтобы вычислить зто выражение, функция тесте() вызывается п-1 раз ло тех пор, пока значение переменной и нс станет равным 1. Часть 1. Основы языка С++: подмножество С В этот момент начнется послсдователыюе выполнение операторов хаесха, озпосяшихся к разным вызовам Функции васек(). При вычислении Факториала числа 2 первый вызов функции васек[) порождасз второй вызов этой функции — теперь уже с аргументом, равным 1. Второй вызов вернет число 1, которое будет умножено иа 2 (исходпос значение аргумента а) Попробуйте сами проследить за вызовами функции басах(] при вычислении Факториала числа 3. (Чтобы увидезь уровень кюкаого вызова функции васек(), можно вставить в пее вызов функции ряваед(] и вывести промежуточные резулшаты.) Когда Функция вызывает саму себя, в стеке размешается новый набор сс локальных переменных и параметров, и Функция выполняется с начала.
Рекурсивиыи вызов пс создает новую копию Функции. Копируются лишь переменные, с которыми опа работает. При каждом рекурсивном возврате управления копии переменных и параметров удаляются из стека, а выполнение программы возобновляется с места вызова Функции. Рекурсивные Функции ~)апомииают телескоп, который то раскладывается, то складывается Как правило, рекурсивные функции цезиачителыю умспьшают размер кода и ненамного повышают эффскгивпосгь использования памяти по сравнению с ес итеративными аналогами. Кроме того, рекурсивные версии большинства функций выполняются несколько медленнее, чем их итеративные эквиваленты. Большое количество рекурсивных вызовов может вызвать переполнение стека.
поскольку при каждом вызове Функции в стеке размешается новый набор се локальных переменных и параметров. При этом программист может пребывать в полном неведении. пока ис произоидет аварийное прекрашспие работы программы. Основное преимушество рекурсивных функций заключается в том, по оии упрошают и делаю~ нагляднее некоторые алгоритмы.
Например, алгоритм быстрой сортировки трудно реализовать итеративным способом. Кроме того, нскоторыс проблемы, связанные с искусственным интеллектом, легче решить, применяя рекурсию. В заключение отметим, что некоторым людям легче думать рскурсивио, чем итеративно. Создавая рекурсивную функцию, необходимо предусмотреть условный оператор, например, оператор ье, с помошью которого можно прекрати~ь выполнение рекурсивных вызовов. Если э~о~о ие сделать, функция може~ бесконечна вызывать саму себя. Отсутствие условия остановки рекурсии является весьма распространенной ошибкой. Для отслеживания промежуточных результатов следует широко применять Функцию ркдаедО. Это позволит сохранять контроль за вычислениями и прервать выполнение программы, если возникла ошибка. Прототилы функций В языке С++ все функции должны быть объявлены до своего первого вызова.
Обычно для этого используются ярототиям функции (Гцпсг)оп ргогогурез). В исходном варианте языка С прототипов ие было. Опи были добавлены при разработке стандар- та, в котором настоятельно рекомендуется использовать прототипы. Следует заметить, что это лишь пожелание, а ие категорическое требование. В то же время, в языкс С++ прототипы являются абязатеиьными. В этой книге все функции имеют полиыс прототипы. Это позволяет выполнять более строгую проверку типов, примерно так же„как в языке Разса!. Если в программе используются прототипы, компилятор мо- жет обнаружить несоответствия между типами аргументов и параметрами функций, а также несовпадение их количества, Обший вид прототипа таков.
1 тии имя ф>'акции(тия имя яараметра/, тия имя яаралгетра2, тия амя яараиетра/к) Глава б. Функции Указывать имена параметров не обязательно. Однако они дают компилятору возможность обнаружить имя параметра, тип которого нс совпадает с именем аргумента, поэтому рекомендуется испольэовать имена аргументов в прототипах. Слсдуюшая программа иллюстрирует ценность прототипов. При компиляции вы получи~с сообшение об ошибке, поскольку программа солсржит вызов функции вцх хе () с целочисленным аргументом, в то время как ей следует передать указатель целого типа. (Преобразование целочисленной переменной в указатель не допускается.) /* Прототип функции обеспечивает строгуп проверку типов. */ чозс) вст 'Е Расс *з)з /* Прототип */ 1пе 1п( зс)) ( 1пс хз х -- 10з вс(г 1Е(х)з /* Несовпадение типов */ хеецхп Оз чозг) вг)х зс(1пс *з.) ( *1 = "1 * *аз ) Если определение функции размещено до ее первого вызова, оно само счзггастся прототипом.
Таким образом, программа, приведенная ниже, является совершеюю правилыюй. взпс1цс)е <вес)1о.п> /* Это определение функции служит ее прототипом. */ чо1д 1(1пе а, апе )з) ( ргзпсг("Ъс) ", а Ъ Ь)з ) 1пе взазп(чоЫ! ( й(10,3)з хеецхп оз Поскольку функция еО определена до ее вызова в функции взвап(], отдслызый прототип не нужен. Объединение определения с прототипом чаше всего используют в маленьких программах.
В больших программах такой прием встречается редко, особенно если программа состоит из нескольких файлов. Все програмлзы, приведенные в нашей книге, содержат отдельные прототипы функций, поскольку именно такой сгиль считается правильным. Единственная функция, для которой не требуется прото~ил, — функция вв1п(), поскольку она всегда вызывается первой. Следует учитывать небольшую, но важную разницу между прототипами функций, не имсюших параметров, в языках С и С++. В языке С++ пустой список параметров просто обозначается пустыми скобками. Например, так. Часть!. Основы языка С++: подмножеспю С Ъпе т(); /* Прототип Функции, не имеюатй параметров, '* ' * в языке С++ */ Однако в языке С этот прототип трактуется иначе.
По историческим причинам пустой список параметров в языке С означает отсузлстеие лифе/)мацки о лера/катерах. В зависимости от компилятора это может означать, что функция либо не плюет гира- метров, либо имеет несколько параметров. В языке С прототип функции, нс имевшей параметров, должен содержать ключевое слово еоъд. Вот как должен выглядеть прототип функции во в про)рамме на языке С. $ г1оас д(ъ.оьг)); Это объявление оюбшает компилятору, что функция не имеет параметров. и любой вызов этой функции с какими-либо параметрами является ошибкой.
В языке С++ такой способ объявления пустого списка параметров также допускается, но считается излишним. В языке С++ выражения Е() н Е(тоде) зкенеаленгяны. ( фф Прототипы функций позволягот выявлять ошибки в программе. Кроме того, они гарантируют, что функция не будет вызвана с неверными параметрами. И последнее, поскольку в ранних версиях языка С прототипы не полдсрживались, их применение в программах на С является необязательным. При переводе программ с языка С на язык С++ следует добавить полные прототипы всех функций. Помните, несмотря на то что прототипы в языке С не обязательны, в языке Св+ они необходимы. Следовательно, любая функция в программе на языке С++ должна иметь полный прототип.
Прототипы стандартных библиотечных функций Программа должна содержать прототипы всех вызываемых сганлартных библиотечных функций. Для этого в программу следует включить соответствуюшие заголовочные файлы, поддерживаемые компиляторами языка С/С++. В языке С заголовочные файлы обычно имеют расширения .)ь В языке Сч-ь заголовочные фаилы могут быть как отдельными, так и создаваться самим компилятором. В любом случае, заголовочный файл состоит из двух элементов: определений переменных и функций, используемых в библиотечных функциях, а также прототипов самих библиотечных функций.
Например, заголовочный файл еегт).о.)з включается почти во все программы, рассмотренные в книге, поскольку он содержит прототип функции ртвпея(). Заголовочные файлы для стандартной библиотеки описаны в части !П. Определение списка параметров переменной длины В программе можно объявить функцию с переменным количеством параметров. В большинстве случаев это относится к функции рхдиед(). Чтобы сообшить компилятору, что функция может быть вызвана с разным количеством параметров, объявление ее параметров следует завершить многоточием. Например, прототип, приведенный ниже, означает, что функция ввпсо может иметь по меньшей мере лва параметра и сколько угодно дополнительных параметров (или вовсе не иметь их) $ Ъпе баас(ьпс а, Ъпе )з, ... ); Этот способ объявления параметров используется и в определении функции.
Глава 6. Функции Полный спи авочник по В Ф ! 1 Структуры, объединения, перечисления и Оперетер фребе$ языке С сушествугот пять способов создать пользовательский тип. 8 1. Струклсура (мгисгигс), позволяюшая объелинить группу переменных пол одним именем. Структура часто называется агрегированным типом данных. (Распрострагсспы также термины составной, или совокулнысс, тип.) 2. Битовое лоле (Ьй-бсЫ), представляющее собой вариант структуры и позволяющее работать с отдельными битами. 3. Объединение (ипюп), позволяюшее использовать одну и ту же область памяти для хранения нескольких переменных разного типа.
4, Перечисление (епипюгайоп), представляюшсе собой список именованных целочисленных констант. 5. Ключевое слово еуребеб, даюшсс возможность присваивать сушествуюшему типу новос имя. В языке С+я- полдерживаются все пять способов и добавляются классы, описанные в части П. ,''ВЗОВЩдуЯ~В В языке С++ струюпуры и объединения могут иметь квк объекганоз(ЯЙВЙЙЙЙМ ориенгпироввнные, тек и процедурные своиотве. В этои главе описываются лишь императивные особенности зглик типов Ик объектно-ориентссровенные свойстве будугл рассмотрены позднее.
~® Структуры Структури — зто набор переменных, объединенных обшим именем. Она обеспечивает удобный способ организации взаимосвязанных данных. Объявление струкгяуры создает се шаблон, который можно использовать при создании объектов структуры (т.е. ес экземпляров).
Переменгсые, входяшие в структуру, называются ее членами (гпегпЬегз). (Члены структуры часто называют также ее злементими или полями.) Как правило, все члены структуры логически связаны друг с другом. Например, имя и адрес в списке рассылки естественно представлять в виде структуры. Следуюший фрагмент программы демонстрирует, как объявляется структура, состояшая из полей, в которых хранятся имена и адреса.