Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
L_r_04.doc
Скачиваний:
0
Добавлен:
26.12.2019
Размер:
287.74 Кб
Скачать

Лабораторная работа № 4. Решение задач с использованием функций

1. Цель работы.

Изучить и освоить методы составления функций и обращения к ним при решении задач на языке С++.

2. Подготовка к работе.

Изучить правила составления функций. Необходимо знать основные способы организа­ции взаимодействия основной программы (функции main) и функции, определяемой поль­зователем [лекция 13].

3. Теоретическая часть.

Функция в C++ это логически самостоятельная именованная часть программы, состоящая из нуля (в противном случае) или более операторов, объединённых в исполнимый модуль для решения определённой задачи.

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

Неграмотно написанная функция наряду с аргументами использует и глобальные переменные, которые доступны из любого блока текущего файла.

Для использования функции требуется знать только ее интерфейс (правила обращения и общения). Интерфейс функции определяется ее заголовком, потому что в нем указывается все, что необходимо для ее вызова: имя функции, тип результата, который она возвращает, а также сколько аргументов и какого типа ей нужно передать.

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

Формат простейшего заголовка функции:

тип имя ( [ список_параметров ] );

где спецификация тип - задаёт тип возвращаемого функцией значения. Если указание типа отсутствует, то считается, что функция возвращает значение int. Если вместо типа стоит клю­чевое слово void, то считается, что функция не возвращает в вызывающую программу никакого значения. Особый случай, когда используется тип void* - родовой указатель. В этом случае результат работы функции есть указатель.

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

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

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

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

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

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

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

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

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

  • вызов функции с передачей значений;

  • вызов функции с переда­чей адресов переменных;

  • вызов функции с использованием механизма ссылок при пере­даче параметров;

  • посредством глобальных параметров.

Вызов функции с передачей значений. Этот способ передачи параметров обеспечивает пе­ре­дачу копий переменных в стек, организуемый при активизации функции. При этом обес­печи­вается защита самих переменных от их изменения в функции. Пример:

#include<iostream.h>

int sum(int,int); // объявление функции

void mane(void)

{int a,b,c;

cin >> a >> b;

c=sum(a,b); //передача параметров значений

cout << c << endl;

}

// определение функции

int sum(int d, int l) // заголовок

{ // тело функции

int f;

f=d+l;

return f;// результат передаётся в точку вызова

}

Вызов функции с передачей адресов. Этот способ передачи параметров обеспечивает пере­дачу в стек адресов передаваемых данных, что позволяет функции работать непосредст­венно с данными.

#include<iostream.h>

sum(int,int,int*); // объявление функции

void main( )

{int a,b,c=0;

cin>>a>>b;

sum(a,b,&c); // вызов функции

cout<<c<<endl;

}

void sum(intd,intl,int*f) // определение функции

{

*f=d+l // f – указатель на c

}

Вызов функций с использованием механизма ссылок. Этот способ обеспечивает доступ к передаваемым параметрам посредством определения их альтерна­тивного имени. Например:

#include<iostream.h>

sum(int,int,int&);

void main( )

{

int a,b,c=0;

cin >> a >> b;

sum(a,b,c);

cout << c << endl;

}

//void

sum(int d,int l,int &f)

{

f=d+l; // f- ссылка на c

}

Вызов функции с передачей данных посредством глобальных параметров. Этот способ пе­редачи исходных данных в вызываемую функцию и возвращения результата вычислений путём использования глобальных параметров. Например:

#include <iostream.h>

int a,b,c;

sum(); // объявление функции

main()

{

cin >> a >> b;

sum(); //вызов функции

cout<<c<<endl;

}

sum() // определение функции

{c=a+b; //a,b,c- глобальные переменные

}

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

Объявление значений функции по умолчанию производится путём указания значений ар­гументов в прототипе функции посредством оператора присваивания.

#include<iostream.h>

float ur(float x,float a=0.,float b=0.,float c=0.);

int main()

{float a=1.,b=2.,c=3.,x=0.5,y;

y=ur(x,a,b,c);

cout<<"введены все аргументы"<<"\n";

cout<<y<<"\n";

y=ur(x,a,b);

cout<<"введены x,a и b"<<"\n";

cout<<y<<"\n";

y=ur(x);

cout<<"введен x"<<"\n";

cout<<y<<"\n";

cin>>a;

}

float ur(float x,float a,float b,float c)

{

return a*x*x+b*x+c;}

На экране дисплея мы получим следующие результаты работы вышеприведенной про­граммы.

Введены все аргументы

4.25

введены x,a и b

1.25

введен x

0.

В языке C++ допустима рекурсия. Рекурсия это способ организации вычислительного процесса, при котором процедура или функция может обращаться сама к себе. Покажем рекурсивную реализацию метода быстрой сортировки. В методе используется процедура половинного разделения, применяемая на 1-ом шаге ко всему массиву, а на следующих шагах – к его фрагменту. На каждом шаге образуются две половинки текущего фрагмента, к которым снова применяется процедура разделения. Если массив сортируется по возрастанию, то в левую половинку записываются меньшие значения, а в правую – большие (если по убыванию, то наоборот).

Одну из возможных версий программы покажем на примере:

#include<iostream.h>

void quicksort(float* arr, int left,int right);

void main()

{const int n = 10;

float ar[n];

int i, l, r;

//cputs(«введите данные о значениях исходного массива»);

for(i=0; i<n; i++){

cout<<”введите а[“<<k<<”] исходного массива \n”;

cin>>ar[i];

}

l = 0; r = n – 1; //левая и правая границы начального фрагмента

quicksort(ar, l, n); // вызов функции

for(i=0; i<n; i++)

printf(“ar[ %d ]= %d \n”, i, ar[i]); // cout<<ar[i]<<” “;

}

void quicksort(float * arr, int left, int right)

{int i = left, j = right; //левая и правая границы фрагмента

float middle = arr[(left + right) / 2];

float temp;

while (i < j) {

while (arr[i] < middle) i++;

while (middle < arr[j]) j--;

if (i <= j) { // замена значений

temp = arr[i];

arr[i] = arr[j];

arr[j] = temp;

i++;

j--;

}

}

if(i < right)quicksort(arr, i, right); /* вызов функции для

сортировки правой половины фрагмента массива */

if(left < j)quicksort(arr, left, j); /* для сортировки левой

половины фрагмента */

}

Процедура разделения реализована в виде рекурсивно вызываемой функции quicksort(), в теле которой есть два обращения к самой себе: для сортировки левой половинки теккущего фрагмента и сортроки его правой половинки.

Однако, у рекурсии есть недостатки:

  • такую программу труднее отлаживать, поскольку требуется контролировать глубину рекурсивного обращения;

  • при большой глубине стек может переполниться;

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

Поэтому рекурсию следует применять с осторожностью.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]