Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
62
Добавлен:
02.05.2014
Размер:
543.23 Кб
Скачать

Билет 30

1.

2. Рекурсивная функция

Каждая функция может вызвать саму себя. Действие, состоящее в том, что функция вызывает сама себя, называется рекурсией. Рекурсивной называют функцию, которая прямо или косвенно сама вызывает себя. Именно возможность прямого или косвенного вызова позволяет различать прямую или косвенную рекурсию. При каждом обращении к рекурсивной функции создается новый набор объектов автоматической памяти, локализованных в теле функции. Функция называется косвенно рекурсивной в том случае, если она содержит обращение к другой функции, содержащей прямой или косвенный вызов определяемой (первой) функции. В этом случае по тексту определения функции ее рекурсивность (косвенная) может быть не видна. Если в теле функции явно используется вызов этой же функции , то имеет место прямая рекурсия. Так как при каждом обращении к рекурсивной функции создается новый набор объектов автоматической памяти, локализованных в теле функции, то при использовании рекурсивных алгоритмов с глубокой вложенностью рекурсии может быстро произойти переполнение стека реализации рекурсий, поэтому надежнее использовать итерационные алгоритмы.

Билет 31

2. Функция. Прототип

Функция в C++ объявляется, определяется, вызывается. В разделе, посвящённом структуре программного модуля, в качестве примера мы уже рассматривали синтаксис определения функции. Определение функции состоит из заголовка и тела. Заголовок функции состоит из спецификаторов объявления, имени функции и списка параметров. Тело функции образуется блоком операторов.

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

Если определение функции встречается транслятору до выражения вызова, никаких проблем не возникает. Вся необходимая к этому моменту информация о функции оказывается доступной из её определения:

#include <iostream.h>

void ZZ(int param) // Определение функции.

{

cout << "This is ZZ >> " << param << endl;

}

void main (void)

{

ZZ(10); // Вызов функции. Транслятор уже знает о функции всё.

}

При этом не принципиально фактическое расположение определения функции и выражения её вызова. Главное, чтобы в момент разбора выражения вызова в транслятор знал бы всё необходимое об этой функции. Например, в таком случае:

#include <iostream.h>

#include "zz.cpp"

/*

Препроцессор к моменту трансляции "подключает" определение функции ZZ() из файла zz.cpp.

*/

void main (void)

{

ZZ(125);

}

Файл zz.cpp:

void ZZ(int par1)

{

cout << "This is ZZ " << par1 << endl;

}

Но как только в исходном файле возникает ситуация, при которой вызов функции появляются в тексте программы до определения функции, разбор выражения вызова завершается ошибкой:

#include <iostream.h>

void main (void)

{

ZZ(10);

/* Здесь транслятор сообщит об ошибке. */

}

void ZZ(int param)

{

cout << "This is ZZ " << param << endl;

}

Каждая функция, перед тем, как она будет вызвана, по крайней мере, должна быть объявлена. Это обязательное условие успешной трансляции и вольный перевод соответствующего сообщения об ошибке (Call to undefined function 'ИмяФункции'), выдаваемого транслятором в случае вызова необъявленной функции.

Напомним, что объявление и определение - разные вещи. Объект может быть много раз объявлен, но только один раз определён. Прототип функции при этом играет роль объявления функции. В объявлении функции сосредоточена вся необходимая транслятору информация о функции - о списке её параметров и типе возвращаемого значения. И это всё, что в момент трансляции вызова необходимо транслятору для осуществления контроля над типами. Несоответствия типов параметров в прототипе и определении функции выявляются на стадии окончательной сборки программы. Несоответствие спецификации возвращаемого значения в объявлении прототипа и определении функции также является ошибкой.

#include <iostream.h>

void ZZ(int ppp);

/*

Эта строка требуется для нормальной компиляции программы.

Это и есть прототип функции. Имя параметра в объявлении может

не совпадать с именем параметра в определении.

*/

void main (void)

{

ZZ(125);

}

void ZZ(int par1)

{

cout << "This is ZZ " << par1 << endl;

}

Самое интересное, что и такое объявление не вызывает возражений транслятора.

#include <iostream.h>

void ZZ(int);

/*

Отсутствует имя параметра. Можно предположить, что имя параметра

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

*/

void main (void)

{

ZZ(125);

}

void ZZ(int par1)

{

cout << "This is ZZ " << par1 << endl;

}

Правила грамматики подтверждают это предположение. Ранее соответствующее множество БНФ уже рассматривалось:

ОбъявлениеПараметра ::= СписокСпецификаторовОбъявления Описатель

::= СписокСпецификаторовОбъявления

Описатель

Инициализатор

::= СписокСпецификаторовОбъявления

[АбстрактныйОписатель]

[Инициализатор]

Из этой формы Бэкуса-Наура следует, что объявление параметра может состоять из одного спецификатора объявления (частный случай списка спецификаторов). Так что имени параметра в списке объявления параметров в прототипе функции отводится в букальном смысле роль украшения. Его основное назначение в прототипе - обеспечение легкочитаемости текста программы. Принципиальное значение имеет соответствие типов параметров в определении и объявлении функции.

Попытка трансляции следующего примера программы оказывается неудачной.

#include <iostream.h>

void ZZ(float);// Другой тип параметра.

void main (void)

{

ZZ(125);

}

void ZZ(int par1)

{

cout << "This is ZZ " << par1 << endl;

}

Если функция не возвращает значения, в объявлении и определении обязательно используется спецификатор объявления void.

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

Соседние файлы в папке Програмки на C++