Учебное пособие_С++2015
.pdfint c; //локальная переменная if (a>b)
c=a;
else c=b;
return c; // возвращение результата
}
void main()
{int x,y,z; printf("Введите x и y:");
scanf("%d%d",&x,&y); // ввод двух чисел z=maximum(x,y); // обращение к функции maximum
printf("max=%d\n",z);
}
Рассмотрим пример программы, использующей нетипизированную функцию. Функция Form_matrix будет заполнять целочисленную матрицу размера mxn случайными числами.
#include "stdafx.h" #include "stdlib.h"
#define M 50
void Form_matrix(int A[][M], int m, int n) /*заголовок
функции Form_matrix */
{
int i,j; //локальные переменные
for(i=0;i<m;i++)
for(j=0;j<n;j++) A[i][j]=rand()%100-50;
}
void main()
{int m1,n1,m2,n2; //переменные для задания размерности матриц
int Matr1[M][M],Matr2[M][M]; /*объявление двух матриц размера 50х50 */
printf("Введите размерность Mart1 "); scanf("%d%d",&m1,&n1); // ввод двух чисел
Form_matrix(Matr1,m1,n1); //обращение к функции Form_matrix printf("Введите размерность Mart2 ");
scanf("%d%d",&m2,&n2); // ввод двух чисел
Form_matrix(Matr2,m2,n2); //обращение к функции Form_matrix
…
}
101
Данный пример демонстрирует использование функции Form_matrix дважды в главной функции. При первом обращении к функции произойдет заполнение матрицы Mart1, а при втором обращении – Matr2, так как их имена указаны в соответствующих обращениях к функции. Функция Form_matrix может заполнить матрицу любого размера, не превышающего 50х50. (Конечно, можно было создать динамические матрицы, но это другая история, и мы говорили о ней в параграфе «Динамическая память»).
В теории о подпрограммах-функциях следует остановиться еще на трех важных вопросах:
обращение к нетипизированной и типизированной функциям;
передача параметров в функцию;
возвращение результатов.
Обращение к функции
В простейшем случае, когда функция нетипизированная, для ее вызова достаточно указать ее имя, за которым в круглых скобках через запятую перечислить имена передаваемых в нее параметров. Этот способ вызова функции продемонстрирован в примере с функцией Form_matrix.
Обращение к типизированной функции не является специальным оператором, а включается в состав выражения в операторе присваивания, как это показано в приведенном выше примере с функцией maximum или сразу включается в оператор вывода, если возвращаемое значение далее в программе не используется. В приведенном примере можно было вызов функции maximum сделать так:
printf("max=%d\n", maximum(x,y));
Результат выполнения функции возвращается в точку вызова функции через ее имя.
Пример программы с функцией
Вычислить значение: Z = a5 a 5 ,
2a7
где а - заданное вещественное число.
В этой задаче требуется многократно использовать алгоритм возведения числа в целую степень. Оформим функцию, в которой данный алгоритм можно формально описать как алгоритм накопления произведения.
P xn x x x ... x,
n раз
где i – номер шага вычисления (умножения); n – число шагов.
102
Поскольку в задаче требуется вычислить три раза операцию возведения в степень, то в главной функции main() будет организован вызов этой функции из выражения.
#include "stdafx.h" |
|
|
float ST(float x, int n) |
// начало функции ST |
|
{ |
|
|
int |
i; |
|
float |
P; //локальные переменные i и P |
|
P=1; |
|
|
for( i=1; i<= n; i++) |
|
|
P = P*x; //накопление произведения |
||
return |
P; |
|
} |
// конец функции ST |
void main()
{
float a,Z;
printf("Введите число а:"); scanf("%f",&a);
Z = (ST(a, 5) + ST(1/a, 5))/(2* ST(a, 7)); printf("Z=%f\n", Z);
}
В процессе выполнения программы после ввода заданного числа а вычисляется значение Z по формуле. В данной формуле обращение к функции ST() осуществляется с помощью трех операндов. При вычислении выражения операнд обращения к функции заменяется значением возвращаемого функцией значения. Далее вычисляется значение Z и выводится на экран.
Предыдущую программу можно скомпоновать так:
#include "stdafx.h"
float ST(float , int ); // прототип функции ST
void main()
{float a,Z;
printf("Введите число а:"); scanf("%f",&a);
Z = (ST(a, 5) + ST(1/a, 5))/(2* ST(a, 7));
printf("Z=%f\n", Z);
}
103
// текст функции ST
float ST(float x, int n)
{
int i; float P; P=1;
for( i=1; i<= n; i++) P = P*x;
return P;
}
Передача параметров в функцию
Механизм передачи параметров является основным способом обмена информацией между вызывающей и вызываемой функциями. Напомним, что параметры, перечисленные в заголовке функции, называются формальными, а параметры, указываемые при вызове функции – фактическими. В приведенном выше примере
Z = (ST(a, 5) + ST(1/a, 5))/(2* ST(a, 7));
при обращении к функции ST параметры a, 5, 1/a, 7 – фактические данные, с которыми будет работать функция ST в каждом своем вызове.
В заголовке функции ST
float ST(float x, int n)
x и n - это формальные параметры, которые примут в себя копии значений фактических параметров.
Формальные и фактические параметры должны быть согласованы друг с другом по количеству, типу и порядку следования. Это означает, что количество формальных параметров должно быть равно количеству фактических параметров, и каждый формальный параметр должен иметь тот же тип и занимать в списке то же место, что и соответствующий ему фактический параметр.
Механизмы замены параметров
В языке С++ существует два механизма передачи параметров в функции:
по значению и по адресу.
При передаче по значению в стек заносятся копии значений фактических параметров, и операторы функции работают с этими копиями. Для них при вызове функции в памяти компьютера временно выделяются ячейки, в
которые передаются копии |
значений фактических параметров. При |
|
104 |
выполнении функции значения в этих ячейках могут измениться, однако соответствующие им фактические параметры останутся без изменения. Фактическим параметром, передаваемым по значению, может быть константа, переменная или выражение, что и продемонстрировано в предыдущем примере при вызове функции ST().
При передаче параметров по адресу все действия в функции выполняются непосредственно над фактическим параметром, а не его копией. Поэтому любое изменение формального параметра приводит к изменению соответствующего ему фактического параметра.
Рассмотрим два примера, иллюстрирующих механизмы передачи
параметров: |
|
Пример 1 |
Пример 2 |
#include "stdafx.h" |
#include "stdafx.h" |
void Z (int у) |
void Z (int *у) |
{ |
{ |
y=l; |
*y=1; |
} |
} |
void main() |
void main() |
{ int х; |
{ int х; |
x=0; |
х=0; |
Z(x);//обращение к функции Z |
Z(&x);//обращение к функции Z |
printf("x=%d", x); |
printf("x=%d", x); |
} |
} |
В примере 1 функция Z() содержит формальный параметр у, который передается по значению, поэтому его изменение в процедуре (у=1;) не влияет на значение фактического параметра х. После выполнения программы на экран будет выведено: х=0.
В примере 2 у функции Z() формальный параметр у – это указатель. В него передается при вызове функции Z() адрес фактического параметра x. Это означает, что функция Z() изменяет значение y в той же ячейки памяти, где находится значение фактического параметра x . На экран будет выведено:
х = 1.
Параметры-массивы в функциях
Массивы, так же как и простые переменные, можно передавать в функции в качестве параметров. Так как имя массива – это адрес, то передача массива происходит всегда по адресу.
105
Рассмотрим, например, функцию, вычисляющую среднее значение элементов массива. Желательно сделать ее так, чтобы в нее можно было передавать массив любого размера. В языке С++ функции не могут самостоятельно определять размер массива, поэтому он (размер) должен быть обязательно одним из параметров.
#include "stdafx.h"
int Sum ( int A[], int N )//заголовок функции
{
int i, sum; //локальные переменные sum = 0;
for ( i = 0; i < N; i ++ ) sum += A[i];
return sum/N; //возвращаемое значение
}
void main()
{
int x[5]={1,2,3,4,5}, y[3]={11,22,33};
printf("\n sr x=%d sr y=%d\n", Sum(x,5),Sum(y,3));
}
Обратите внимание, что указан отдельным параметром. а только как A[] или *A.
в заголовке функции Sum размер N массива Нельзя объявлять массив-параметр как A[N],
Если в функцию передаѐтся двумерный массив, то описание соответствующего параметра функции должно содержать количество столбцов; количество строк - несущественно, поскольку фактически передаѐтся указатель. Например, так: int Х[ ][5], или Х[5][5].
Рассмотрим пример функции, перемножающей матрицы А и В; результат - матрица С. По правилам математики перемножать можно матрицы, размеры которых (mxn) и (nxk) соответственно. Результирующая матрица будет иметь размеры (mxk).
const nmax = 50;
void product(int А[][nmax], int В[][nmax],int С[][nmax], int m, int n, int k)
{
/* m - число строк в матрице А;
n - число строк в матрице В и число столбцов в матрице А; k - число столбцов в матрице В. */
106
for (int i=0; i< m; i++) for (int j=0; j< k; j++)
{
С[i][j]=0;
for (int l=0; l< n; l++) С[i][j] + = А[i][l]*В[l][j];
}
}
Функция product в заголовке содержит список из шести формальных параметров. Первые три – для приема адресов трех матриц: двух исходных и результирующей. Последующие три параметра примут копии значений размеров матриц.
В приведѐнном примере есть недостаток - здесь заранее фиксируется максимальная размерность матриц. Но использоваться может только часть памяти.
Если мы хотим передать в подпрограмму отдельный элемент массива, то в качестве соответствующего ему формального параметра указывается простая переменная того же типа.
Возвращение результатов
Результат работы функции возвращается в точку вызова с помощью оператора
return [выражение];
Таким образом, функция может вернуть только одно скалярное значение. Если функция должна вернуть несколько результатов, то этот возврат реализуется с помощью указателей, т.е. параметров, передаваемых по адресу.
Теория создания функций, рассмотренная в данном параграфе, является базовой и будет достаточной для начинающих программистов. Для более глубоко изучения данного вопроса следует обратиться, например [4]. Но, даже изучив теорию в таком объеме, у нас есть возможность научить вас собирать собственные библиотечные файлы.
Примеры программирования задач с использованием подпрограмм
Задача 1
Даны два вектора: x = {хi}; i =1,8 и y ={yi};i =1,10 .
Вычислить значение: D = yx , где x mx sx ; y my sy ;
тх, ту - максимальные компоненты векторов x и y sx, sy - средние значения компонент векторов x и y
соответственно; соответственно.
107
Решение:
#include "stdafx.h" #include <math.h>
float Mod_Otk(float *a, int n) /* типизированная функция для нахождения максимального компонента и среднего значения в любом массиве */
{
float maxi, sa, Da; //описание локальных переменных int i;
maxi =-10000; sa=0;
for (i =0;i<n;i++)
{
if (a[i]>maxi) maxi= a[i]; sa += a[i];
}
sa = sa/n;
Da= fabs(maxi - sa); return Da;
}
void main()
{ float X[10],Y[10]; int i;
float Dx, Dy, D; printf("Bведитe массив X:\n"); for (i =0;i<8;i++)
scanf("%f",&X[i]); printf("Bведитe массив Y:\n"); for (i =0;i<10;i++)
scanf("%f",&Y[i]);
Dx = Mod_Otk(X, 8);//вызов функции Mod_Otk для массива X Dy = Mod_Otk(Y,10);//вызов функции Mod_Otk для массива Y
D = Dx/Dy; printf("D=%f\n",D);
}
Задача 2
Даны две матрицы: А = {a i j }5x6 и В = {b i j}4x7.
Вычислить разность: С = КА - KB, где КА и KB - количество положительных элементов в матрицах А и В соответственно.
108
#include "stdafx.h"
int CP(float D[7][7], int m, int n) /*типизированная функция
для подсчета |
количества положительных элементов в любой матрице */ |
{ int |
i, j, KD; |
KD=0;// инициализация счетчика for (i=0;i<m;i++)
for (j=0;j<n;j++)
if (D[i][j]>0) KD ++; /*добавление в счетчик единицы, если элемент матрицы окажется >0 */
return KD; // возвращение результата в точку вызова
}
int main() |
|
{ |
|
float |
A[7][7], B[7][7]; |
int i,j, C;
printf("Введите матрицу А\n"); for (i=0;i<5;i++)
for (j=0;j<6;j++) scanf("%f",&A[i][j]);
printf("Введите матрицу B\n"); for (i=0;i<4;i++)
for (j=0;j<7;j++) scanf("%f",&B[i][j]);
C= CP(A, 5,6)- CP(B, 4, 7); //вызовы функции СР printf("C=%d\n", C);
return 0;
}
Задача 3
На плоскости декартовыми координатами заданы 10 точек:
{x1,y1},{x2,y2}, ...,{х1 0 ,у1 0 }.
Вывести полярные координаты точки, имеющей наибольший полярный радиус. Вычисление полярных координат одной точки оформить подпрограммой. Расчетные формулы для вычисления полярных координат следующие:
a2 b2 ; arctg ba ; , где а и b -декартовы координаты точки.
109
Решение:
#include "stdafx.h" #include<math.h>
void PK(float a, float b, float *ro, float *fi)
/* безтиповая функция для расчета полярных координат точки */
{
*ro = sqrt(a*a + b*b); *fi = atan(b/a);
}
int main()
{
float X[10], Y[10]; // масcивы для декартовых координат точек float R[10], F[10]; // масcивы для полярных координат точек
int i, N; float maxR;
printf("Введите абсциссы 10 точек\n"); for (i=0;i<10;i++) scanf("%f",&X[i]); printf(" Введите ординаты 10 точек\n"); for (i=0;i<10;i++) scanf("%f",&Y[i]); maxR = 0;
for (i=0;i<10;i++)
{
PK(X[i],Y[i],&R[i],&F[i]);
if (R[i]>maxR) // поиск максимального радиуса
{
maxR =R[i];
N=i; /*запоминаем номер точки, радиус которой больше, чем у предыдущих */
}
}
printf("romax=%f fimax=%f\n", R[N],F[N]);/* вывод полярных координат точки с номером N */
return 0;
}
Задача 4
Для заданных квадратных матриц: A = {a i j}3x3 и В = {bi j}4х4 вычислить симметричные им матрицы по правилу:
110