Программа 17. Рекурсивная печать целого
// Файл Recurs.cpp
#include <iostream.h>
#include <conio.h>
// printd: печатает n как целое десятичное число
void printd(int n)
{
if(n < 0){ // Если число отрицательное,
cout.put('-'); // печатаем знак числа,
n = -n; // делаем число положительным
}
if(n / 10) // Если в n более одной цифры,
printd(n / 10); // печатаем старшие цифры, иначе
cout.put('0' + n % 10); // выводим последнюю цифру
}
void main()
{
int number;
cout << "\nВведите число: "; cin >> number;
printd(number);
}
Символы цифр ’0’, ’1’, …, ’9’ занимают в кодовой таблице непрерывный участок и расположены в порядке возрастания их значения. Код цифры ’0’ равен 48, у цифры ’1’ код 49 и т.д., поэтому выражение
n % 10 + ’0’ дает правильный код последней десятичной цифры числа n. Например, если n = 321, то n % 10 + '0' = 1 + 48 = 49, что равно коду цифры ’1’, и эту цифру изобразит на экране функция put в соответствии со значением своего аргумента.
При каждом вызове рекурсивной функции создается свой собственный набор локальных переменных, в том числе и формальных параметров. Сам же код рекурсивной функции существует в единственном экземпляре. Таким образом, рекурсивная функция, не закончив обработку одного набора данных, переключается на обработку другого, но затем возвращается и завершает прерванную обработку.
Полезно выполнить данную программу в пошаговом режиме, наблюдая за работой рекурсивной функции, за значениями формального параметра n и состоянием экрана. Разберем работу функции printd. Пусть для number введено значение 123, то есть первый вызов функции имеет вид printd(123). Работу функции поясняет табл. 12. Углубление в рекурсию, когда функция вызывает сама себя, происходит, пока обрабатываемое число остается многозначным. Последний вызов printd происходит для однозначного числа 1, на экран выводится цифра ’1’ и работа 3-го вызова завершается, после чего управление передается во 2-й вызов на инструкцию:
cout.put(n % 10 + '0');
которая выводит предпоследнюю цифру, затем происходит возврат в
1-й вызов, печатается последняя цифра исходного числа и работа printd завершается.
Таблица 12. Работа рекурсивной функции
Перегруженные имена функций
Если функции делают примерно одинаковую работу над объектами разных типов, целесообразно давать им одинаковые имена. Использование одного имени для операции, выполняемой над различными типами, называется перегрузкой. Например, одно имя + используется для сложения целых чисел и чисел с плавающей точкой, хотя им соответствуют совершенно разные машинные команды.
Пусть в программе требуется вычислять абсолютную величину переменных типа int и типа double. Для этого можно написать две функции с одинаковыми именами, что и сделано в следующей программе.
Программа 18. Перегрузка функций
// Файл Module.cpp
#include <iostream.h>
#include <conio.h>
// module: возвращает абсолютную величину целого n
int module(int n)
{
if(n < 0)
return -n;
return n;
}
// module: возвращает абсолютную величину плавающего x
double module(double x)
{
if(x < 0)
return -x;
return x;
}
int main()
{
cout << "module(-3.14) = " << module(-3.14f) << "\n";
cout << "module(-3) = " << module(-3) << "\n";
getch();
return 0;
}
Компилятор различает функции с одним именем по количеству и типам аргументов, а также по типу возвращаемого значения. При выборе подходящей функции из многих перегруженных компилятор ищет такую, которая имеет наилучшее соответствие типов формальных и фактических параметров. Программа печатает:
module(-3.14) = 3.14
module(-3) = 3
Это показывает, что для числа с плавающей точкой -3.14f, имеющего тип float, вызывается функция
double module(double x);
а для целого -3 функция
int module(int n);
Аргументы функций по умолчанию
У функций общего назначения часто больше аргументов, чем требуется в простых случаях. В программе 19 рассмотрена функция печати целого val. В качестве аргумента функции передается основание системы счисления base, в которой следует печатать целое, но предполагается, что в большинстве случаев целые будут печататься в виде десятичных чисел, поэтому значением по умолчанию для base указано 10.