Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Metodichka_PZ.doc
Скачиваний:
1
Добавлен:
01.04.2025
Размер:
2 Мб
Скачать
☆

Общие сведения

В языке C++ программа состоит из функций. Функции - относительно самостоятельные фрагменты программы, спроектированные для решения конкретных задач и снабженные именем [2]. С одной из них, главной, мы уже знакомы. Функции аналогичны программам в миниатюре и имеют общее на­звание подпрограммы. Главное отличие простой функции от главной в том, что с главной функции начинается выполнение программы, а все остальные функции вызываются либо главной, либо из других функций. Применение подпрограмм дает возможность сэкономить память за счет размещения мно­гократно повторяющихся частей программ в функции, а также возможность конструировать программу как, набор отдельных подпрограмм, что, в свою очередь, позволяет работать группе программистов над сложной задачей. Даже в том случае, если некоторая задача решается в одной программе и од­ним программистом, лучше оформить ее решение в виде подпрограмм, по­скольку функции повышают уровень модульности, облегчает чтение, вне­сение изменений и коррекцию ошибок в программе. Пусть необходимо напи­сать программу, которая выполняет следующие действия:

вводит массив чисел;

определяет среднее арифметическое чисел;

определяет местоположение максимального элемента;

заменяет максимальный элемент массива средним арифметиче-

ским;

выводит обработанный массив.

Операторную часть такой программы можно записать следующим образом:

{..............

vvodmas(mas);

s

87

r=sredmas(mas);

max=maxmas(mas);

zamena(mas);

vivod(mas);

…………..

}

Как видно из приведенного фрагмента, программа разделена по смыслу на пять подпрограмм, которые еще следует написать. В программе на языке C++ описание функций может располагаться либо до функции main, либо после функции main. Никакая функция не может быть описана внутри дру­гой функции. Кроме того, функции могут быть описаны в отдельном файле и подключены к программе с помощью директивы препроцессора include. Каждая функция описывается один раз, а вызываться на выполнение может многократно. В языке C++ описание должно обязательно предшествовать вызову функции. Если функция описана после функции, из которой она вызывается, то до вызова функции должен быть помещен прототип функции.

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

В общем случае структуру подпрограммы можно представить следующим образом:

заголовок функции

тело функции

Общая форма записи заголовка функции:

<тип> <имя> (<список формальных параметров>)

З

88

десь <имя> - правильный идентификатор, который используется для обращения к функции;

<тип> - тип возвращаемого функцией результата;

<список формальных параметров> - включает в себя параметры,

необходимые для нормального интерфейса, т. е. позволяет передавать дан­ные в подпрограмму и из подпрограммы. Список формальных параметров необязателен и может отсутствовать (круглые скобки опускать нельзя). Если он есть, то может включать в себя параметры, передаваемые по значению, указателю и ссылке. Например:

void sum(float a, float b, float *s)

void sk(int *a, int c, float d)

float f (float x, float &y)

int ab(int a)

void par(void)

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

Вызов функции производится при помощи оператора вызова функций:

<имя>(<список фактических параметров>);

Он записывается в том месте программы, где по логике решения задачи требуется выполнение операторов, определенных в теле функции. После вы­полнения операторов тела функции управление передается оператору, сле­дующему за оператором вызова.

Следует заметить, что таким образом вызываются функции типа void, т.е. функции, не возвращающие никакого значения. Функции же, возвращающие значения типа int, float и т. д., вызываются упоминанием имени в вы­ражении аналогично стандартным функциям sin, cos и другим. Поэтому в теле таких функции обязательно должен присутствовать оператор возврата return. После ключевого слова return записывается выражение, значение которого вставится вместо имени функции в точке вызова. Функция не мо­жет возвращать массив или функцию. В качестве результата, возвращаемого функцией, могут быть значения только простого и ссылочного типа.

Пусть имеется следующее описание функции:

int max (int a, int b)

{

if(a>b) return a;

else return b;

}

Функция max может использоваться в выражении

с=mах(5,3);

m=max(c-d,c+d)+2*max(c*d,c%d);

л

89

ибо

printf(''max=%d'', max(c*d, c*d);

В данном примере функция возвращает значение целого типа, фактиче­ские параметры - тоже целого типа. Параметры передаются по значению. При первом обращении к функции max формальный параметр а заменяется фактическим параметром, которому соответствует целое число 5, а формаль­ный параметр b - числом 3. Возвращает же функция число 5, и это число присваивается переменной с. При втором и третьем обращениях в качестве фактических параметров передаются выражения. Они сначала вычисляются, и уже значения выражений подставляются в качестве формальны параметров. В четвертом случае функция max вызывается из функции printf.

К списку параметров предъявляются следующие требования:

количество формальных параметров должно быть равно количеству фактических параметров;

порядок следования фактических и формальных параметров дожжен быть один и тот же;

тип каждого фактического должен совпадать с типом соответствующего формального параметра.

Рассмотрим пример использования функции. Даны вещественные числа a,b и с. Необходимо вычислить значения х и у по формулам:

//Пример рr27

#include<stdio.h>

#include<stdlib.h>

#include <conio.h>

float f(float af, float bf, float cf)

{

return (af+bf)/cf;

}

void main()

{

float a,b,c,x,y;

printf("\n Введите a,b,c");

scanf(" %f%f%f ", &a, &b, &c);

x=f(a,b,c*c+10);

y=f(c,a,b*b+ 5);

printf(''\nx=%5.2f y=%5.2f\n'',x,y);

}

Д

90

ля вызова функции здесь используется оператор присваивания. Параметры a,b,c*c+10 в момент первого обращения к функции - фактические параметры, а параметры c,a,b*b+ 5 - фактические параметры в момент вто­рого обращения к функции. Пользователь должен следить за порядком сле­дования фактических параметров, смысл их использования зависит от порядка их перечисления в списке. Они подставляются вместо формальных пара­метров af, bf, сf в заголовке функции, а затем над ними выполняются необ­ходимые действия. Параметры в данном примере передаются по значению. В этом случае под af, bf, сf cf выделяется дополнительная память, и туда копи­руются значения соответствующих фактических параметров. Таким образом, в подпрограмме используются копии фактических параметров, что и позво­ляет в качестве последних применять выражения. Так как при передаче параметров по значению в функции работаем с копией, то изменение формаль­ного параметра не приводит к изменению соответствующего фактического параметра. Если же в изменении фактического параметра возникает необхо­димость, то параметры передаются по указателю или ссылке.

Для этой же задачи составим программу с использованием параметров, передаваемых по указателю (рr28). // Пример рr28

#include <stdio.h>

#include <conio.h>

void f(float af, float bf, float cf, float *yf)

{

*yf=(af+bf)/cf;

}

void main()

{

float a,b,c,x,y;

printf("\n Введите a,b,c");

scanf(" %f%f%f ",&a,&b,&c);

f(a,b,c*c+10,&x);

f(c,a,b*b+5,&y);

printf("\nx=%5.2f y=%5.2f\n",x,y);

getch();

}

В

91

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

Третий способ передачи параметров - это передача параметров по ссыл­ке. В языке С этого способа передачи параметров не было. Понятие ссылка появилось только в C++. Ссылочные переменные описываются следующим образом [8]:

<тип> &<имя>=<инициализирующее выражение>

<тип> - любой тип C++;

<имя> - идентификатор переменной ссылочного типа;

<инициализирующее выражение> - выражение, определяющее ме­стоположение в памяти. Например:

int y=16;

int &х=у;

printf("x=%d\n",x);

у=12;

printf("x=%d\n",x);

Результат работы:

x=16

x=12

Ссылочная переменная x определяет местоположение переменной у в памяти в инициализирующем выражении int &x=y. Переменная х обрабаты­вается как "нормальная" переменная типа int. При обращении к такой переменной нет необходимости в операции снятия ссылки, Когда переменной у присваивается значение 12, то переменная x принимает это новое значение, поскольку она определяет местоположение переменной у в, памяти. Если в этот пример добавить строку х=10, то при этом, значение переменной у ста­нет равным 10. Поскольку х - это ссылка на переменную у, то любое изме­нение переменной х приведет, к изменению переменной у. Ссылочная пере­менная при объявлении должна быть инициализирована. Объявление int &x; недопустимо, поскольку неизвестно на что указывает переменная х. Теперь рассмотрим, как же передается параметр по ссылке на примере той же зада­чи, что и в предыдущей программе рr28. // Пример рr29

#include <stdio.h>

#include <conio.h>

void f(float af, float bf, float cf, float &yf)

{

yf=(af+bf)/cf;

}

void main()

{

float a,b,c,x,y;

printf("\n Введите a,b,c");

scanf(" %f%f%f ", &a, &b, &c);

f(a,b,c*c+10,&x);

92

f(c,a,b*b+5,&y);

printf("\nx=%5.2f y=%5.2f\n",x,y);

getch();

}

Здесь используется ссылочный параметр yf. Для формального параметра float &yf инициализирующего выражения не требуется. Инициализирующее выражение подставляется при вызове функции. В частности, при вызове функции f(a,b,c*c+10,x) инициализирующее выражение принимает вид float &yf=x. При вызове функции f(a,b,c*c+10,y) инициализирующее вы­ражение принимает вид float &yf=y. В примере рr29 версия функции бо­лее "натуральна", нежели версия в примере рr28, так как нет необходимости в операции разыменовывания.

Рассмотрим еще один пример использование функции. Инженеру часто приходится решать алгебраические или трансцендентные уравнения, что мо­жет представлять самостоятельную задачу или являться составной частью более сложных задач. В обоих случаях практическая ценность численного метода в значительной мере определяется быстротой и эффективностью по­лучения решения. В данном примере решаем трансцендентное уравнение (нелинейное уравнение, содержащее тригонометрические функций или дру­гие специальные функции, например Ig х или ). Методы решения таких уравнений делятся на прямые и итерационные. Первые позволяют найти решение непосредственно с помощью формул и всегда обеспечивают получе­ние точного результата. В итерационных методах задается процедура решения в виде многократного применения некоторого алгоритма. Полученное решение всегда является приближенным, хотя может быть сколько угодно близким к точному. Итерационные методы наиболее удобны для реализации на компьютере. Пусть необходимо найти корень уравнения f(х)=0 на интервале [а,b] с точностью е. При этом функция f(x) на этом интервале не­прерывна и монотонна, f(x)=x-sin(x)/cos(x), a<b, e>0. Графическая схема алгоритма представлена на рис. 25, программа - в рrЗ0.

// Пример pr30

#include <stdio.h>

#include <conio.h>

#include <math.h>

float f(float xf)

{

return xf-sin(fx)-0.25;

}

void main()

{

float a,b,e,x,x1,x2;

printf("\n Введите a,b,e");

93

scanf(" %f%f%f ", &a, &b, &e);

Рис. 25

if(f(a)*f(b)>0) printf("\nKopней нет\n");

else

{ x1=a;

x2=b;

while(fabs(x2-x1)>e)

{ x=(x1+x2)/2;

if(f(x1)*f(x)>0)

x1=x;

else x2=x;

}

printf("Корень уравнения = %f\nf(x) в этой точке = %f ",x1,f(x1));

}

getch();

}

С

94

начала проверяется, есть ли корень на заданном интервале, т. е. меня­ются ли знаки на конах интервала f(a) * f(b) > 0. Taк как функция на ин­тервале монотонна и непрерывна, то смена знака указывает на существова­ние корня. Если корень существует, то обозначаем концы интервала новыми идентификаторами xl и х2. Проверяем, достаточно ли мал интервал, на котором находится корень, fabs(x2-x1)>e. Если интервал больше точности, то находим среднее значение х на интервале. В зависимости от того, на какой половине [x1,x] или [x,x2], происходит смена знака, интервал соответствующим образом сужается. Как только длина интервала становится равной или меньшей е, процесс завершается и любой из концов интервала можно считать корнем уравнения с точностью е.

В языке C++ функции можно размещать в отдельном файле. В этом случае файл, из которого функция вызывается должен содержать директиву препроцессора #include "имя_файла.срр".

Контрольные вопросы

1. Для чего предназначены функции?

2. Как описываются ссылочные переменные?

3. Как происходит передача параметров в функцию по значению?

4. Как происходит передача параметров в функцию по указателю?

5. Как происходит передача параметров в функцию по ссылке?

6. Какие требования предъявляются к списку формальных параметров?

7. Какого типа значения может возвращать функция?

8. Для чего предназначен оператор return?

9. Какие значения будут напечатаны в результате работы следующей программы: #include<stdio.h>

int а=1,b=2, с=3;

int f(int a, int &b, int *c)

{

a+=2; b++; *c=*c+1;

return a+b;

}

void main()

{

c=f(a+1,b,&c);

printf("\na=%5d b=%5d c=%5d\n",a,b,c);

int y=f(c+5,a,&b);

printf("\na=%5d y=%5d b=%5d c=%5d\n",a,y,b,c);

}

95

Варианты заданий

Табл. 6

96

Окончание табл.6

97

ЛАБОРАТОРНАЯ РАБОТА №8

СТРУКТУРЫ

Цель работы:

Приобретение навыков обработки таблиц, ведомостей, документации.

Порядок выполнения работы:

В соответствии с поставленной задачей необходимо разработать графическую схему алгоритма, составить программу и отладить ее в среде С++, подготовить отчет, ответить на контрольные вопросы и защитить лабораторную работу перед преподавателем.