Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Конспект С++ (Часть 2).doc
Скачиваний:
16
Добавлен:
10.09.2019
Размер:
816.64 Кб
Скачать

9. Функции и структура программы

Создание и использование функций. Вызов функции (аргументы функции) и возврат значения. Передача параметров по значению, по ссылке. Глобальные и локальные переменные. Классы памяти и область действия. Автоматические переменные. Внешние переменные. Статические переменные. Внешние статические переменные. Регистровые переменные. Функции с переменным количеством аргументов. Использование функции как параметра другой функции. Рекурсия. Представление программы в виде набора функций. Многофайловая структура программы.

Использование функций позволяет:

  1. значительно упростить разработку сложных программ;

  2. сократить объем текста программы и генерируемого результирующего кода программы;

  3. значительно упростить отладку и модификацию программ;

  4. распределить работу над одной программой между различными исполнителями программистами.

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

9.1. Создание и использование функций Процедурный подход к разработке программ

В качестве примера рассмотрим следующую задачу.

Имеется числовая матрица размера n на m. Необходимо вывести на экран индексы всех седловых точек. Седловой точкой является элемент матрицы со значением минимальным в строке и максимальным в столбце, в котором находится элемент, или наоборот – максимальным в строке и минимальным в столбце.

Общий план решения этой задачи может быть, например, таким:

  1. Подготовка данных для решения задачи.

  2. Решение задачи и вывод результатов.

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

#include "stdafx.h"

#include <iostream>

using namespace std;

const int n = 5, m = 5; // n - количество строк, m - количество столбцов матрицы

int A[n][m]; // А - исходная матрица

void Подготовка_данных()

{

}

void Решение_и_вывод_результатов()

{

}

int _tmain(int argc, _TCHAR* argv[])

{

Подготовка_данных();

Решение_и_вывод_результатов();

system("Pause");

return 0;

}

Теперь можно перейти к последовательной реализации каждой из этих функций.

Для подготовки исходных данных необходимо:

  1. заполнить массив A некоторыми данными и

  2. вывести содержимое матрицы на экран, для того чтобы в дальнейшем проконтролировать правильность решения задачи.

Реализуем эти действия также с помощью соответствующих функций:

void Заполнение_массива()

{

}

void Вывод_массива()

{

}

void Подготовка_данных()

{

Заполнение_массива();

Вывод_массива();

}

Для заполнения данными матрицы воспользуемся датчиком случайных чисел, сама процедура заполнения двумерного массива значениями настолько проста, что дальнейшей детализации не требует:

void Заполнение_массива()

{

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

for (int j = 0; j < m; ++j)

A[i][j] = rand() % 3; // Генерация сл. чисел в диапазоне [0, 2]

}

Здесь мы заполнили матрицу значениями 0, 1 и 2. При таких значениях элементов матрицы легче получить матрицу, содержащую седловые точки.

Вывод значений двумерного массива на экран также не должно вызывать затруднений:

void Вывод_массива()

{

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

{

for (int j = 0; j < m; ++j)

cout << setw(4) << right << A[i][j];

cout << endl;

}

}

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

Теперь мы получили следующую программу:

#include "stdafx.h"

#include <iostream>

#include <iomanip>

using namespace std;

const int n = 5, m = 5; // n - количество строк, m - количество столбцов матрицы

int A[n][m]; // А - исходная матрица

void Заполнение_массива()

{

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

for (int j = 0; j < m; ++j)

A[i][j] = rand() % 3;

}

void Вывод_массива()

{

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

{

for (int j = 0; j < m; ++j)

cout << setw(4) << right << A[i][j];

cout << endl;

}

}

void Подготовка_данных()

{

Заполнение_массива();

Вывод_массива();

}

void Решение_и_вывод_результатов()

{

}

int _tmain(int argc, _TCHAR* argv[])

{

Подготовка_данных();

Решение_и_вывод_результатов();

system("Pause");

return 0;

}

Если запустить эту программу на выполнение, на экран будет выведена матрица, содержащая 5 строк и 5 столбцов с элементами, значения которых принадлежат диапазону [0, 2] .

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

Перебор элементов матрицы можно выполнить с помощью двух вложенных циклов.

Предположим, что имеется функция bool Это_седловая_точка(int i, int j), которая возвращает значение true, если элемент матрицы А [i] [j] является седловой точкой. Тогда реализация функции Решение_и_вывод_результатов() будет выглядеть так:

void Решение_и_вывод_результатов()

{

cout << endl;

int Счетчик = 0; // Счетчик седловых точек

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

for (int j = 0; j < m; ++j)

if (Это_седловая_точка(i, j))

{

cout << setw(4) << i << setw(4) << j << endl;

++ Счетчик;

}

cout << "\nКоличество седловых точек: " << Счетчик << endl;

}

Реализуем функцию Это_седловая_точка. Для того чтобы определить является ли элемент матрицы с индексами i и j седловой точкой, необходимо знать минимальное и максимальное значения в строке с индексом i значения, а также минимальное и максимальное значения в столбце с индексом j. Допустим, что у нас имеются четыре функции:

int Максимум_строки (int Строка)

int Минимум_строки (int Строка)

int Максимум_столбца (int Столбец)

int Минимум_столбца (int Столбец)

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

bool Это_седловая_точка(int Строка, int Столбец)

{

return ((A[Строка][Столбец] == Максимум_строки(Строка)) &&

(A[Строка][Столбец] == Минимум_столбца(Столбец))) ||

((A[Строка][Столбец] == Минимум_строки(Строка)) &&

(A[Строка][Столбец] == Максимум_столбца(Столбец)));

}

Теперь реализуем оставшиеся 4 функции по определению максимальных и минимальных значений элементов:

int Максимум_строки(int Строка)

{

int Максимум = A[Строка][0];

for (int i = 1; i < n; ++i)

if (A[Строка][i] > Максимум)

Максимум = A[Строка][i];

return Максимум;

}

int Минимум_строки(int Строка)

{

int Минимум = A[Строка][0];

for (int i = 1; i < n; ++i)

if (A[Строка][i] < Минимум)

Минимум = A[Строка][i];

return Минимум;

}

int Максимум_столбца(int Столбец)

{

int Максимум = A[0][Столбец];

for (int i = 1; i < n; ++i)

if (A[i][Столбец] > Максимум)

Максимум = A[i][Столбец];

return Максимум;

}

int Минимум_столбца(int Столбец)

{

int Минимум = A[0][Столбец];

for (int i = 1; i < n; ++i)

if (A[i][Столбец] < Минимум)

Минимум = A[i][Столбец];

return Минимум;

}

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

int _tmain(int argc, _TCHAR* argv[])

{

char c;

setlocale(0, "");

do

{

Подготовка_данных();

Решение_и_вывод_результатов();

}

while (cout << "\n\t\t\tПродолжим? (нет - Esc)", c = _getch(),

cout << "\n\n", c != 27);

return 0;

}

Итак, получена следующая рабочая программа:

#include "stdafx.h"

#include <iostream>

#include <iomanip>

#include <conio.h>

using namespace std;

const int n = 5, m = 5; // n - количество строк, m - количество столбцов матрицы

int A[n][m]; // А - исходная матрица

void Заполнение_массива()

{

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

for (int j = 0; j < m; ++j)

A[i][j] = rand() % 3;

}

void Вывод_массива()

{

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

{

for (int j = 0; j < m; ++j)

cout << setw(4) << right << A[i][j];

cout << endl;

}

}

void Подготовка_данных()

{

Заполнение_массива();

Вывод_массива();

}

int Максимум_строки(int Строка)

{

int Максимум = A[Строка][0];

for (int i = 1; i < m; ++i)

if (A[Строка][i] > Максимум)

Максимум = A[Строка][i];

return Максимум;

}

int Минимум_строки(int Строка)

{

int Минимум = A[Строка][0];

for (int i = 1; i < m; ++i)

if (A[Строка][i] < Минимум)

Минимум = A[Строка][i];

return Минимум;

}

int Максимум_столбца(int Столбец)

{

int Максимум = A[0][Столбец];

for (int i = 1; i < n; ++i)

if (A[i][Столбец] > Максимум)

Максимум = A[i][Столбец];

return Максимум;

}

int Минимум_столбца(int Столбец)

{

int Минимум = A[0][Столбец];

for (int i = 1; i < n; ++i)

if (A[i][Столбец] < Минимум)

Минимум = A[i][Столбец];

return Минимум;

}

bool Это_седловая_точка(int Строка, int Столбец)

{

return ((A[Строка][Столбец] == Максимум_строки(Строка)) &&

(A[Строка][Столбец] == Минимум_столбца(Столбец))) ||

((A[Строка][Столбец] == Минимум_строки(Строка)) &&

(A[Строка][Столбец] == Максимум_столбца(Столбец)));

}

void Решение_и_вывод_результатов()

{

cout << endl;

int Счетчик = 0; // Количество седловых точек

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

for (int j = 0; j < m; ++j)

if (Это_седловая_точка(i, j))

// Выводим индексы i и j седловой точки и увеличиваем

// количество седловых точек на 1

{

cout << setw(4) << i << setw(4) << j << endl;

++ Счетчик;

}

cout << "\nКоличество седловых точек: " << Счетчик << endl;

}

int _tmain(int argc, _TCHAR* argv[])

{

char c;

setlocale(0, "");

do

{

Подготовка_данных();

Решение_и_вывод_результатов();

}

while (cout << "\n\t\t\tПродолжим? (нет - Esc)", c = _getch(),

cout << "\n\n", c != 27);

return 0;

}

А вот фрагмент результатов ее работы:

Здесь мы получили матрицу с 4-мя седловыми точками – выделены красным.

Проанализируем ход проектирования программы.

Использованный метод проектирования называется проектированием “сверху вниз” или методом пошаговой детализации. Этот метод основан на последовательном разбиении решения задачи на более простые функционально независимые подзадачи. Такая детализация продолжается до тех пор, пока реализация очередной подзадачи в виде исполняемой программы не становится очевидной. Естественным способом реализации этого метода является представление каждой подзадачи в виде функций, взаимодействующих между собой на уровне данных. Основная часть разработки программы проводилась на чисто логическом уровне, практически без использования инструкций языка программирования. Постепенное дробление алгоритма на более простые подзадачи привело к тому, что реализация каждой из функции, соответствующих этим подзадачам уже не вызывает никаких затруднений при их реализации на языке программирования. Дальнейшая модификация программы (например, реализация ручного ввода исходных данных) связана с коррекцией отдельных функций и не влечет за собой переделки всей программ. Отладка разработанных по такой методике программ может осуществляться по частям (на уровне отдельных функций). Разработку различных частей программы (отдельных ее функций) можно поручить различным исполнителям. Таким образом, использование функций приводит к очевидным преимуществам.