Использование команд передачи управления
Эти дополнительные команды, рассмотренные выше, предназначены для использования в управляющих операторах или для моделирования других управляющих структур. Операторы break и continue предназначены для использования в цикле и позволяют пропустить последующие операторы программы. Условный оператор (?:) позволяет сжать определенные выражения типа if..else в одну строку.
Один совет: подумайте дважды перед использованием каждой команды передачи управления (за исключением, конечно, return). Используйте их в тех случаях, когда они представляют наилучшее решение, но помните, что чаще всего вы можете решить возникшую перед вами проблему без их помощи. Особенно избегайте оператора goto: операторы break или continue наверняка заменят его вам.
Функции
Вы изучили, как выполняются программы, содержащие условные и итеративные операторы. Сейчас настало время задать нам вопрос: а что же делать, если я вдруг захочу выполнить различные группы операторов, использующие различные наборы данных или находящиеся в различных частях программы? Ответ прост: объединяйте эти группы операторов в подпрограммы, к которым вы будете обращаться по необходимости.
В С++ все подпрограммы рассматриваются как функции. Теоретически, каждая функция возвращает некоторое значение. Практически же, значения, возвращенные большинством функций игнорируются и целое семейство новых определений языка С++ (включая проект стандарта С++, предложенный ANSI, и С++) позволяют описывать и использовать в языке функции типа void, которые никогда не возвращают значений.
В С++ вы можете и описывать и определять функцию. Когда вы описываете функцию, то даете всем остальным программам (включая главный модуль main) информацию о том, каким образом должно осуществляться обращение к этой функции. Когда вы определяете функцию, вы присваиваете ей имя, по которому к ней будет осуществляться обращение, и указываете, какие конкретно действия она будет выполнять. Внимательно изучите этот модифицированный вариант RATIO.CPP:
/* Описание функций */
void get_parms(float *p1, float *p2);
float get_ratio(float divident, float divisor);
void put_ratio(float qiotient);
const float INFINITY = 3.4E+38;
/* Главная (main) функция: стартовая точка
программы */
void main()
{
float a, b, ratio;
char ch;
do
{
get_parms(&a, &b); /* ввод параметров */
ratio = get_ratio(a, b); /* вычисление частного */
put_ratio(ratio); /* печать выходного результата*/
printf ("Нажми ‘q’ для выхода или любую клавишу для
продолжения\n");
}
while((ch = getch()) != ‘q’);
} /* конец main */
/* Определение функций */
void get_parms(float *p1, float *p2)
{
printf("Введите два числа: ");
scanf("%f %f", p1, p2);
}
float get_ratio(float divident, float divisor)
{
if(divisor == 0.0)
return(INFINITY);
else
return(divident / divisor);
}
void put_ratio(float ratio)
{
if(ratio == INFINITY)
printf("Внимание! Деление на ноль!\n");
else
printf("Результат деления двух чисел: %f\n",
ratio);
}
Анализ программы
Первые три строки программы – описание функций; они вводятся для того, чтобы описать как тип функций, так и порядок следования их параметров с целью избежания ошибок.
Следующая строка описывает константу с плавающей точкой, которой присваивается имя INFINITY (в соответствии с принятым в С++ соглашением имена констант состоят из заглавных букв). Эта константа имеет очень большое положительное значение – наибольшее из допустимых для типа float, и используется как флаг возникновения деления на ноль (divide-by-zero).
Заметьте, что, несмотря на то, что константа описана здесь, она “видима” внутри всех функций (включая главную функцию).
Далее следует функция main (главная функция), которая является основным телом вашей программы. Каждая программа на С++ обязательно содержит функцию с именем main. Когда ваша программа начинает выполняться, вызывается функция main и дальнейшее выполнение программы продолжается под ее управлением. Когда выполнение функции с именем main заканчивается, то завершается и вся программа, после чего управление передается интегрированной среде С++ или, если вы выполняли программу непосредственно в DOS, то самой DOS.
Функция main может быть расположена в любом месте программы, наиболее часто это первая функция программы. Это обусловлено тем, что расположение функции main в начале программы облегчает ее чтение, описание прототипов функций и различных глобальных переменных. Это, в свою очередь, позволяет облегчить поиск и документирование функций по всей программе.
После main следует фактическое определение трех функций, прототипы которых были описаны выше: get_parms, get_ratio и put_ratio. Рассмотрим отдельно каждое из определений.
Функция get_parms. Функция get_parms не возвращает никакого значения, так как ее тип мы определили как void. Это было сделано в связи с тем, что функция служит лишь для чтения двух значений и сохранения их в некотором месте. В котором? Сейчас дадим некоторые пояснения. Итак, мы передаем в get_parms два параметра. Эти параметры суть адреса, по которым должны быть размещены считанные функцией значения. Обратите внимание! Оба параметра не являются данными типа float, но являются указателями на переменные типа float. Другими словами, мы работаем непосредственно с адресами памяти по которым размещены переменные типа float.
В соответствии с этим и организовано обращение к функции из main: когда мы вызываем get_parms из main, параметрами функции являются &a и &b (адреса), а не текущие значения a и b. Заметьте также, что вся информация, используемая при обращении к функции scanf, находится непосредственно внутри функции get_parms и перед параметрами p1 и p2 не стоят знаки операций адресации. Почему? Потому, что p1 и p2 уже сами по себе адреса; они являются адресами переменных a и b.
Функция get_ratio. Функция get_ratio возвращает результат (типа float) обработки двух значений типа float, передаваемых ей (divident и divisor). Возвращенный функцией результат зависит от того, равно значение переменной divisor нулю или нет. Если значение divisor равно нулю, get_ratio возвращает константу INFINITY. Если нет - действительное значение частного двух чисел. Обратите внимание на вид оператора return.
Функция put_ratio. Функция put_ratio не возвращает значение, т.к. ее тип объявлен как void. Она имеет ровно один параметр – ratio, который используется для того, чтобы определить, какое именно сообщение следует выводить на экран дисплея. Если ratio равно INFINITY, значит значение частного двух чисел считается неопределенным, в противном случае значение ratio выводится на экран дисплея как результат работы программы.
Глобальные описания
Константы, типы данных и переменные, описанные вне функций (включая main), считаются глобальными ниже точки описания. Это позволяет использовать их внутри функций в пределах всей программы вследствие того, что они уже описаны и известны во всех функциях ниже точки описания. Если вы переместите описание INFINITY в конец программы, то компилятор выдаст сообщение о том, что им обнаружены две ошибки: одна из них – в get_ratio, а другая – в put_ratio. Причина ошибок – использование неописанного идентификатора.
Функции могут быть размещены в программе в различном порядке и считаются глобальными для всей программы, включая встроенные функции, описанные до их использования. Старайтесь корректно использовать функции, которые еще вами не определены и не описаны; когда компилятор обнаружит функцию, которую прежде он не встречал, он определит тип значений, возвращаемый функцией как int. Если вы ранее определили тип возвращаемых ею значений как, например, char*, то компилятор выдаст ошибку несоответствия типов данных.
4.8.3. Описание функции void
В С++ существует стандартный тип void, который представляется как разновидность “нулевого” типа. Любая функция, не возвращающая явно какое-либо значение, может быть объявлена как функция типа void. Заметим, что большинство программ, использующих динамическое распределение памяти (например, malloc), описываются как имеющие тип void. Это означает, что они возвращают нетипизированный указатель, значение которого вы затем (в С++) можете присвоить указателю любого типа данных без предварительного преобразования типов (хотя преобразования типов лучше использовать повсеместно, для сохранения совместимости).
4.8.4. Оператор return
Существует два основных способа использования оператора return.
Во-первых, в том случае, когда функция возвращает значение, и вам необходимо использовать его в зависимости от того, какое значение возвращается в вызывающую программу, например:
int imax(int a, int b);
{
if (a > b) return(a);
else return(b);
}
Здесь функция использует оператор return для возвращения максимального из двух переданных ей значений.
Второй способ использования оператора return состоит в возможности выхода из функции в некоторой точке до ее завершения. Например, функция может определить условие, по которому производится прерывание. Вместо того, чтобы помещать все основные операторы функции внутрь оператора if, для выхода можно использовать оператор return. Если функция имеет тип void (т.е. не возвращает никакого значения), можно написать return без возвращаемого значения.
Рассмотрим модификацию программы imin, предложенной ранее:
int imin(int list[ ], int size)
{
int i, minindx, min;
if (size <= 0) return (-1);
}
В этом примере, если параметр size меньше либо равен нулю, то массив list пуст, в связи с чем оператор return вызывает выход из функции.
Заметим, что в случае ошибки возвращается значение –1. Т.к. –1 никогда не может быть индексом массива, вызывающая программа регистрирует факт возникновения ошибки.