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

Voprosy_k_ekzamenu_po_informatike

.pdf
Скачиваний:
41
Добавлен:
09.06.2015
Размер:
2.24 Mб
Скачать

26. ПОНЯТИЕ ПОДПРОГРАММЫ, НАЗНАЧЕНИЕ ПОДПРОГРАММ, ИСПОЛЬЗОВАНИЕ ПОДПРОГРАММ.

Подпрограмма – самостоятельная структурная единица, имеющая имя и реализующая некоторый вспомогательный алгоритм, многократно используемый в основной программе или в других подпрограммах с различными значениями некоторых величин, называемых параметрами. Чтобы использовать подпрограмму необходимо: её объявить, описать, а затем использовать обращение к ней в программе.

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

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

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

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

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

51

27. ПОДПРОГРАММЫ В С/С++, ФУНКЦИИ, ВОЗВРАЩАЮЩИЕ ЗНАЧЕНИЕ И НЕ ВОЗВРАЩАЮЩИЕ ЗНАЧЕНИЕ, ПРИМЕРЫ.

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

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

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

int func(short a, short b) { int c = a;

int d = b; return c * d;

}

Эта функция возвращает значение типа int, равное произведению аргументов типа short. С помощью оператора return осуществляется возврат, но она способна передать только одно значение.

Если функция возвращает значение, то вызов функции рассматривается как выражение, значение которого равно величине, возвращаемой функцией.

Функции, не возвращающие значение. В качестве имени типа значения, возвращаемого функцией: указывает на то, что функция не возвращает значения, а вызов такой функции является void-выражением. Тело такой функции не должно содержать операторов return с выражениями. Пример:

void func(short a, short b) { cout >> a + b;

}

52

28. ПЕРЕДАЧА ПАРАМЕТРОВ В ПОДПРОГРАММУ, ПАРАМЕТРЫ ВХОДНЫЕ И ВЫХОДНЫЕ, ПАРАМЕТРЫ, ПЕРЕДАВАЕМЫЕ ПО ЗНАЧЕНИЮ И ПО АДРЕСУ.

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

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

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

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

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

При передаче параметра по указателю в объявлении функции перед именем параметра указывается операция разадресации *, и при обращении к параметру в теле функции используется эта же операция. При вызове функции перед именем соответствующего фактического параметра указывается операция взятие адреса &. Пример:

void func (int *a) { (*a)++;

53

}

int main() {

int x = 10; func (&x);

}

При передаче параметра по ссылке в объявлении функции перед именем параметра указывается операция взятия адреса &. В этом случае в теле функции и при вызове функции операция разадресации выполняется неявным образом, т.е. без участия программиста. Пример:

void func (int &a) { a++;

}

int main() {

int x = 10; func (x);

}

54

29. ИСПОЛЬЗОВАНИЕ ПОДПРОГРАММ, ПАРАМЕТРЫ ФОРМАЛЬНЫЕ, ЛОКАЛЬНЫЕ, ГЛОБАЛЬНЫЕ, ОБРАЩЕНИЯ К ПОДПРОГРАММАМ, ФАКТИЧЕСКИЕ ПАРАМЕТРЫ.

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

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

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

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

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

55

30. ПЕРЕДАЧА ПАРАМЕТРОВ-МАССИВОВ В ПОДПРОГРАММУ, ПРИМЕРЫ.

Передача массива. Когда массив используется в качестве аргумента функции, передается только адрес массива, а не копия всего массива. При вызове функции с именем массива в функцию передается указатель на первый элемент массива. (Надо помнить, что в С имена массивов без индекса - это указатели на первый элемент массива.) Параметр должен иметь тип, совместимый с указателем. Имеется три способа объявления параметра, предназначенного для получения указателя на массив: объявление без определенного параметра, с определенным параметром и через указатель.

Передача с определенным параметром. Пример:

void func (int mas[100])

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

Передача без определенного параметра. Следующий способ состоит в объявлении параметра для указания на безразмерный массив, как показано ниже:

void func (int mas[])

Тут mas объявлен как целочисленный массив неизвестного размера. Поскольку С не предоставляет проверку границ массива, настоящий размер массива не имеет никакого отношения к параметру (но, естественно, не к программе). Данный метод объявления также определяет mas как целочисленный указатель.

Передача через указатель. Пример:

void func (int *mas)

Он допустим, поскольку любой указатель может быть индексирован с использованием [], если он является массивом. (На самом деле массивы и указатели очень тесно связаны друг с другом.) Все три метода объявления параметра приводят к одинаковому результату - указателю. С другой стороны, элемент массива используется как аргумент, трактуемый, как и другие простые переменные.

56

31. ПЕРЕДАЧА ПАРАМЕТРОВ-ФУНКЦИЙ В ПОДПРОГРАММУ.

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

Применение. Для лучшего понимания причин использования обратного вызова рассмотрим простую задачу выполнения следующих операций над списком чисел: напечатать все числа, возвести все числа в квадрат, увеличить все числа на 1, обнулить все элементы. Ясно, что алгоритмы выполнения этих четырёх операций схожи — это цикл обхода всех элементов списка с некоторым действием в теле цикла, применяемый к каждому элементу. Это несложный код, и в принципе можно написать его 4 раза. Но давайте рассмотрим более сложный случай, когда список у нас хранится не в памяти, а на диске, и со списком могут работать несколько процессов одновременно и необходимо решать проблемы синхронизации доступа к элементам. В этом случае задача обхода всех элементов списка будет довольно сложным кодом, который не хотелось бы копировать несколько раз. Правильнее создать функцию общего назначения для обхода элементов списка и дать возможность программистам абстрагироваться от того, как именно устроен алгоритм обхода и писать лишь функцию обратного вызова для обработки отдельного элемента списка.

Пример:

typedef int(*func)(int a, int b);

int call_func(int a, int b) { return a + b;

}

void function(int a, int b, func f) { int sum = f(a, b);

std::cout << "Sum = " << sum << std::endl;

}

int main() {

function(5, 2, &call_func);

}

57

32. РЕКУРСИВНЫЕ ФУНКЦИИ, ПРИМЕРЫ.

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

unsigned long F(short n) { if (n==0 || n==1)

return 1;

else return n*F(n-1);

}

Данный пример рекурсии называется прямой рекурсией. Косвенная рекурсия вызывает себя через вспомогательную функцию.

Любую рекурсивную функцию можно преобразовать в обычную функцию. И наоборот: практически любую функцию можно преобразовать в рекурсивную, если выявить рекуррентное соотношение между вычисляемыми с помощью функции значениями.

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

Пример программы с рекурсией, подсчитывающей факториал введенного числа.

#include <iostream> using namespace std;

int i = 1; // инициализация глобальной переменной для подсчёта колва рекурсивных вызовов

unsigned long int result; // глобальная переменная для хранения возвращаемого результата рекурсивной функцией

unsigned long int factorial(unsigned long int f) {

if (f == 1 || f == 0) // базовое или частное решение return 1;

cout << "Step\t" << i << endl; i++;

cout << "Result= " << result << endl;

result = f * factorial(f - 1); // функция вызывает саму себя, причём её аргумент уже на 1 меньше

return result; } int main() {

58

int n; // локальная переменная для передачи введенного числа с клавиатуры

cout << "Enter n!: "; cin >> n;

cout << n << "!" << "=" << factorial(n) << endl; // вызов рекурсивной функции

return 0;

}

Вывод:

Enter n!: 6 Step 1 Result= 0 Step 2 Result= 0 Step 3 Result= 0 Step 4 Result= 0 Step 5 Result= 0 6!=720

59

33. ПОНЯТИЕ СТРУКТУРНОГО ПРОГРАММИРОВАНИЯ, ЭТАП ПРОЕКТИРОВАНИЯ КОМПОЗИЦИЯ И ДЕКОМПОЗИЦИЯ, ПОНЯТИЕ СТАТИЧЕСКОЙ И ДИНАМИЧЕСКОЙ СТРУКТУРЫ ПРОГРАММЫ, СПЕЦИФИКАЦИЯ ПРОГРАММЫ.

Структурное программирование — методология разработки программного обеспечения, в основе которой лежит представление программы в виде иерархической структуры блоков. Предложена в 1970-х годах Э. Дейкстрой и др. В соответствии с данной методологией любая программа строится без использования оператора goto из трёх базовых управляющих структур: последовательность, ветвление, цикл; кроме того, используются подпрограммы. При этом разработка программы ведётся пошагово, методом «сверху вниз».

Проектирование «сверху вниз» упрощённо можно писать так:

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

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

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

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

60