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

Паппас К., Мюррей У. - Visual C++ 6. Руководство разработчика - 2000

.pdf
Скачиваний:
288
Добавлен:
13.08.2013
Размер:
4.96 Mб
Скачать

//

//exit2.cpp

//Вариант предыдущей программы, в котором вместо

//инструкции return применяется функция exit() .

#include <iostream.h> #include <stdlib.h> #define LIMIT 30

int main () {

int irow, irequested_qty, iscores [LIMIT]; float fsum =0,imin_score, imax_score, faverage; cout << "\nВведите число значений ряда: ";

cin >> irequested_qty; if(irequested_qty > LIMIT) {

cout<< "\nВы можете ввести не более " << LIMIT<< " значений.\n" cout<< "\n >>> Программа завершена. <<<\n"; exit(EXIT FAILURE);

}

for(irow = 0; irow < irequested_qty; irow++) {

cout << "\nВведите " << irow+1 << "-и элемент ряда: cin >> iscores[irow];

}

for(irow = 0; irow < irequested_qty; irow++) fsum = fsum + iscores[irow];

faverage = fsum/(float)irequested_qty; imin_score = imax_score = iscores[0];

for(irow = 0; irow < irequested_qty; irow++) { if(iscores[irow] > imax_score) imax_score = iscores[irow];

if(iscores[irow] < imin_score) imin_score = iscores[irow]; }

cout<<

"\nМаксимальное значение =

"

<<

imax_score;

cout<<

"\nМинимальное значение

=

"

<<

imin_score;

cout<<

"\nСреднее значение

=

"

<<

faverage;

exit(EXIT_SUCCESS); )

Функция atexit( )

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

/*

*atexit.с

Вэтой программе на языке С демонстрируется способ задания процедур,

*выполняемых при завершении программы, а также показывается, как влияет

*порядок их регистрации на очередность выполнения.

*/

#include <stdio.h> #include <stdlib.h>

void atexit f nl (void) ;

void atexit_fn2(void); void atexit_fn3(void) ; int main()

{

atexit(atexit_fnl); atexit(atexit_fn2); atexit(atexit_fn3);

printf("Программа atexit запущена.\n");

111

printf("Программа atexit завершена.\n\n") printf (">>>>>>>>>>>

<<<<<<<<<<<\n\n") ; return (0);

}

void atexit_fnl(void) {

printf("Запущена функция atexit_fnl.\n"); } void atexit_fn2(void) (

printf("Запущена функция atexit_fn2.\n"); 1 void atexit_fn3(void) {

printf ("Запущена функция atexit_fn3An") ; )

При выполнении программы на экран будет выведена следующая информация:

Программа atexit запущена. Программа atexit завершена.

>>>>>>>>>>> <<<<<<<<<<<

Запущена функция atexit_fn3. Запущена функция atexit_fn2. Запущена функция atexit_fnl.

Единственным аргументом функции atexit() является имя регистрируемой функции, которая будет вызвана при завершении выполнения программы. Эта функция будет вызываться независимо от того, завершается ли программа естественным путем, как в данном примере, или с помощью функции exit().

В действительности функция atexit() ведет список функций, которые будут выполнены при завершении программы. Причём порядок выполнения функций противоположен порядку их регистрации. То есть первая зарегистрированная функция будет выполняться последней, а последняя — первой. Вот почему сообщение о запуске функции atexit_fnЗ () появилось раньше, чем сообщение о запуске функции atexit_fnl (). Как правило, "финальные" функции используются для удаления из памяти динамически созданных объектов. Поскольку один объект мог быть создан на основе другого, удаление их из памяти осуществляется в обратном порядке.

112

Глава 7. Функции

Прототипы функций

o Синтаксис объявления функции o Способы передачи аргументов o Правила видимости переменных

oРекурсия

Аргументы функций

oФормальные и фактические аргументы

o Аргументы типа void o Аргументы типа char o Аргументы типа int o Аргументы типа float

o Аргументы типа double

oМассивы в качестве аргументов

Типы значений, возвращаемых функциями

oТип результата: void

o Тип результата: char o Тип результата: bооl o Тип результата: int o Тип результата: long o Тип результата: float

oТип результата: double

Аргументы командной строки

oТекстовые аргументы

o Целочисленные аргументы

oАргументы с плавающей запятой

Дополнительные особенности функций

oМакроподстановка функций

o Перегрузка функций

oФункции с переменным числом аргументов

Область видимости переменных

oПопытка получить доступ к переменной вне ее области видимости

113

o Оператор расширения области видимости

Краеугольным камнем, лежащим в основе языков С и C++, являются функции. В данной главе рассматриваются основные концепции создания и использования функций. Вы познакомитесь с понятием прототипа функции, которое было введено в стандарте ANSIС. На многочисленных примерах будет показано, как работать с аргументами различных типов и как создавать функции, возвращающие значения различных типов. Вы также узнаете, как с помощью стандартных параметров argc и argv получать аргументы командной строки в функции main(). Кроме того, будет рассказано об уникальных возможностях функций в языке. C++.

Основную часть программного кода в C/C++ составляют функции. Они позволяют разбивать программу на отдельные блоки или модули. Таким образом, модульное программирование обязано своим появлением именно функциям. Под модульным программированием подразумевается создание программ из отдельных, самостоятельно отлаживаемых частей, совокупность которых образует единое приложение. Например, один модуль может выполнять функцию ввода данных, другой — реализовывать вывод данных на печать, а третий — отвечать за сохранение данных на диске. По сути, программирование на языках С и C++ как раз и заключается в написании функций. Любая программа на этих языках содержит, по крайней мере, одну функцию — main(). От того, насколько успешно будут проработаны функции, зависит эффективность и надежность работы программы.

В данной главе рассматривается много примеров программ, которые помогут вам лучше уяснить принципы создания функций. Многие из этих программ, помимо пользовательских, используют также встроенные библиотечные функции C/C++, которые значительно расширяют возможности программирования на этих языках.

Прототипы функций

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

Синтаксис объявления функции

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

Вэтой книге мы придерживаемся нового стиля, синтаксис которого таков:

тип_результата имя_функции(тип_аргумента необязательное_имя_аргумента[, ...]);

Функция может возвращать значения типа void, int,float и т.д. Имя функции может быть произвольным, но желательно, чтобы оно указывало на ее назначение. Если для выполнения функции нужно предоставить ей некоторую информацию в виде аргумента, то в круглых скобках должен быть указан тип аргумента и, при необходимости, его имя. Тип аргумента может быть любым. Если аргументов несколько, их описания (тип плюс имя) разделяются запятыми. Не будет ошибкой указание в прототипе только типа аргумента, без имени. Такая форма записи прототипов применяется в библиотечных функциях.

Описание функции представляет собой часть программного кода, которая, как правило, следует за телом функции main(). Синтаксис описания следующий:

тип_результата имя_функции(тип_аргумента имя_аргумента[, ...])

{

тело функции )

114

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

/*

*prototyp.c

*Эта программа на языке С содержит описание функции.

*Функция находит сумму двух целочисленных аргументов и возвращает

*результат в виде целого числа.

*/

#include <stdio.h>

int iadder(int ix, int iy) ; /* прототип функции */ int main () {

int ia = 23; int ib = 13; int ic;

ic = iadder(ia, ib) ; printf("Сумма равна %d\n",ic) ; return(0);

}

int ladder(int ix, int iy) /* описание функции */

{

int iz;

 

iz = ix + iy;

 

return(iz);

/* возвращаемое значение */

}

 

Функция в нашем примере называется iadder(). Она принимает два целочисленных аргумента и возвращает целочисленное значение. В соответствии со стандартом ANSI С рекомендуется, чтобы прототипы всех функций размешались в отдельных файлах заголовков. Вот почему библиотеки .функций распространяются вместе с файлами заголовков. Но в простых программах вроде той, которую мы только что рассмотрели, допускается описание прототипа функции прямо в тексте программы.

Описание функции iadder() в программе на языке C++ будет выглядеть идентично:

//

//prototyp.cpp

//Эта программа на языке C++ содержит описание функции.

//Функция находит сумму двух целочисленных аргументов и возвращает

//результат в виде целого числа.

//

#include <iostream.h>

int ladder(int ix, int iy); // прототипфункции int main() {

int ia = 23; int ib = 13; int ic;

ic = iadder(ia,ib) ;

cout<< "Сумма равна " << ic<< "\n"; return (0); }

int ladder(int ix, int iy) // описаниефункцииint iz; iz = ix + iy; return(iz); // возвращаемое значение

}

Способы передачи аргументов

115

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

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

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

/*

*ref.c

*Эта программа на языке С демонстрирует использование указателей

*в качестве аргументов функции.

*/

#include <stdio.h>

int ladder(int *pix, int *piy); int main ()

int ia = 23; int ib = 13; int ic;

ic = iadder(&ia,Sib) ; printf("Сумма равна %d\n", ic); return(0); }

int ladder (int*pix, int *piy)

{

int iz;

iz = *pix + *piy; return(iz); }

В языке C++ при передаче аргументов можно вместо указателей использовать непосредственно ссылки. Это гораздо удобнее и упрощает текст программы, так как ссылки тоже указывают на аргумент, но не требуют использования оператора раскрытия указателя. Вот пример той же программы на языке C++:

//

//ref.cpp

//Эта программа на языке C++ демонстрирует использование ссылок

//в качестве аргументов функции.

//

#include <iostream.h>

int iadder (int Srix, int Sriy) ; int main ()

{

int ia = 23; int ib = 13; int ic;

ic = iadder (ia,ib) ;

cout<< "Сумма равна " << ic<< "\n"; return (0);

)

int ladder(int Srix, int sriy) I int iz;

iz = rix + riy; return(iz); }

116

Обратите внимание на отсутствие операторов взятия адреса при вызове функции и операторов раскрытия указателей в теле функции. В качестве аргументов функции используются ссылки rixи riy.

В C++ не допускается использование ссылок на ссылки, ссылок на битовые поля, массивов ссылок и указателей на ссылки.

Правила видимости переменных

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

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

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

::переменная

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

Рекурсия

Рекурсия возникает в том случае, когда функция вызывает саму себя. Рекурсивные алгоритмы можно использовать для элегантного решения некоторых задач, из которых одна из наиболее распространенных — нахождение факториала числа. Факториалом называется произведение всех целых чисел от единицы до заданного. Например:

8!=8*7*6*5*4*3*2*1=40320

Обратите внимание на используемый тип данных — double, который позволяет работать с числами порядка 1Е+308. Особенностью факториала является то, что даже для сравнительно небольшого числа результат может быть огромным. Например, факториал числа 15 равен

1307674368000.

/*

*factor.с

*Эта программа на языке С демонстрирует применение

*рекурсии при вычислении факториала.

*/

#include <stdio.h>

double dfactorial(int inumber); int main ()

{

int Inumber =15; double dresult;

dresult = dfactorial(inumber);

printf ("Факториал%d равен%15.0f\n"; inumber, dresult) ; return(0);

}

double dfactorial(int inumber)

{

if (inumber <= 1) return(1.0);

117

else

return(inumber * dfactorial(inumber-1));

}

Аргументы функций

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

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

Формальные и фактические аргументы

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

Рассмотрим следующий фрагмент программы на языке С:

printf("Пример шестнадцатеричного %х и восьмеричного %о значения", ians);

Хотя в строке форматирования указано два аргумента, в наличии имеется только один. Когда функции предоставляется меньше аргументов, чем содержится в списке параметров, недостающим присваиваются неопределенные значения. Во избежание ошибок в C++ предусмотрено задание аргументам стандартных значений. Тогда, если при вызове функции соответствующий аргумент не указан, ему автоматически будет присвоено значение по умолчанию. Вот пример прототипа функции на языке C++:

int имя_функции(int it,float fu = 4.2,int iv = 10)

Если в вызове функции не будут указаны аргументы fu и/или iv, то им по умолчанию будут присвоены значения 4,2 и 10 соответственно. Стандарт языка C++ требует, чтобы аргументы, для которых заданы значения по умолчанию, находились в конце списка формальных аргументов. В нашем примере допустимы следующие варианты вызова функции:

имя_ функции(10) имя_ функции(10,15.2)

имя_функции(10,15.2,8)

Если аргумент fu не будет задан, установка аргумента iv также станет невозможной.

Аргументы типа void

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

/*

*fvoid.с

*Эта программа на языке С содержит пример функции, не принимающей

*никаких аргументов .

*/

#include <stdio.h> #include <math.h> void voutput (void) ;

118

int raain() {

printf("Этa программа вычисляет квадратный корень числа. \n\n"); voutput () ;

return (0);

}

void voutput(void) { int it = 12345; double du;

du = sqrt(it);

printf("Квадратный корень числа %dравен %f \n", it, du); }

В функции voutput() вызывается стандартная библиотечная функция sqrt(), объявленная в файле МАТН.Н. Данная функция возвращает квадратный корень своего аргумента в виде значения типа double.

Аргументы типа char

В следующем примере в функции main() считывается символ, введенный с клавиатуры, и передается в функцию voutput(), которая выводит его на экран. Считывание символа осуществляется функцией _getch(). В языке С есть ряд похожих на нее библиотечных функций: getc(), getchar () и _getche (). Данные функции можно использовать и в языке C++, хотя всех их инкапсулирует в себе объект cin, управляющий вводом данных. Функция _getch() запрашивает символ, поступающий со стандартного устройства ввода (как правило, это клавиатура), и записывает его в переменную типа char без эха на экране.

/*

*fchar.с

*Эта программа на языке С считывает символ, введенный с клавиатуры,

*и передает его в функцию voutput(),осуществляющую вывод нескольких

*копий символа на экран.

*/

#include <stdio.h> #include <conio.h> void voutput(char c) ; int main() {

char cyourchar; printf("Введите символ: "); cyourchar = _getch(); voutput(cyourchar);

return (0); }

void voutput(char c) < int j ;

for (j = 0; j < 16; j++) printf("\nБыл введен символ %с ", с); )

Спецификатор %с в функции printf() указывает, что выводится единственный символ.

Аргументы типа int

В следующем примере введенная пользователем длина ребра куба передается в функцию vside(), которая на основании полученного значения вычисляет площадь грани куба, его объем и площадь поверхности.

/*

*fint.c

*Эта программа на языке С предназначена для вычисления площади грани

*и поверхности куба, а также его объема. Функция vside() принимает

*целочисленное значение, содержащее длину ребра куба.

*/

119

#include <stdio.h> void, vsidefint is); int main()

int iyourlength;

printf ("Введите длину ребра куба: ") ; scanf("%d",&iyourlength) ;

vside (iyourlength) ; return (0);

}

void vside(int is) {

int iarea, ivolume, isarea;

iarea = is * is; ivolume = is * is * is; isarea = 6 * iarea; printf("\nДлина ребра куба: %d\n", is);

printf("Площадь грани куба: %d\n", iarea); printf("Объем куба: %d\n", ivolume); printf("Площадь поверхности куба: %d\n", isarea); }

Аргументы типа float

В следующем примере в функцию vhypotenuse() передаются два аргумента типа float, определяющие длину катетов прямоугольного треугольника. В функции вычисляется длина гипотенузы vhypotenuse().

/*

*ffloat.с

*Эта программа на языке С вычисляет длину гипотенузы

*прямоугольного треугольника.

*/

#include <stdio.h> #include <math.h>

void vhypotenuse(float ft,float fu); int main()

{

float fxlen, fylen;

printf("Введите длину первого катета: "); scanf("%f",&fxlen);

printf("Введите длину второго катета: "); scanf("%f",&fylen); vhypotenuse(fxlen,fylen);

return (0);

}

void vhypotenuse(float ft,float fu) double dresult;

dresult = hypot((double) ft,(double) fu) ; printf("\nДлина гипотенузы равна%g \n", dresult); }

Функция hypot (), возвращающая длину гипотенузы, объявлена в файле МАТН.Н и принимает аргументы типа double, поэтому параметры fx и fу функции vhypotenuse() должны быть приведены к этому типу.

Аргументы типа double

Следующая программа считывает два числа типа double и возводит первое в степень второго.

/*

*fdouble.с

*Эта программа на языке С возводит число в указанную степень.

*/

120

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