Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Функции с переменным количеством параметровСодержание книги
Поиск на нашем сайте
В языках Си и Си++ допустимы функции, количество параметров у которых при компиляции определения функции не определено. Кроме того, могут быть неизвестными и типы параметров. Количество и типы параметров становятся известными только в момент вызова функции, когда явно задан список фактических параметров. При определении и описании таких функций, имеющих списки параметров неопределенной длины, спецификация формальных параметров заканчивается многоточием. Формат описания функции с переменным списком параметров имеет вид: тип_функции имя_функции (спецификация_явных_параметров,...); Здесь тип_функции - тип возвращаемого функцией значения; имя_функции - имя функции; спецификация_явных_параметров - список спецификаций отдельных параметров, количество и типы которых фиксированы и известны в момент компиляции. Эти параметры можно назвать обязательными. После списка явных (обязательных) параметров ставится необязательная запятая, а затем многоточие, извещающее компилятор, что дальнейший контроль соответствия количества и типов параметров при обработке вызова функции проводить не нужно. Сложность в том, что у переменного списка параметров нет даже имени, поэтому непонятно, как найти его начало и где этот список заканчивается. К сожалению, в программировании волшебство мало распространено, и поэтому каждая функция с переменным списком параметров должна иметь механизм определения их количества и их типов. Принципиально различных подходов к созданию этого механизма всего два. Первый подход предполагает добавление в конец списка реально использованных необязательных фактических параметров специального параметра-индикатора с уникальным значением, которое будет сигнализировать об окончании списка. В теле функции параметры последовательно перебираются, и их значения сравниваются с заранее известным концевым признаком. Второй подход предусматривает передачу в функцию значения реального количества фактических параметров. Значение реального количества используемых фактических параметров можно передавать в функцию с помощью одного из явно задаваемых (обязательных) параметров. В обоих подходах и при задании концевого признака, и при указании числа реально используемых фактических параметров переход от одного фактического параметра к другому выполняется с помощью указателей, т.е. с использованием адресной арифметики. Проиллюстрируем сказанное примерами. Следующая программа включает функцию с изменяемым списком параметров, первый из которых (единственный обязательный) определяет число действительно используемых при вызове необязательных фактических параметров. //Программа 6.2 #include "stdafx.h" #include <iostream> //Функция суммирует значения своих параметров типа int long summa(int k,...){ // k - число суммируемых параметров int *pik = &k; long total = 0; for(; k; k--) total += *(++pik); return total; } void main() { std::cout<< "\n summa(2, 6, 4) = " << summa(2,6,4); std::cout<< "\n summa(6, 1, 2, 3, 4, 5, 6) = " << summa(6,1,2,3,4,5,6); getchar(); } Результат выполнения программы: summa(2, 6, 4) = 10 summa(6, 1, 2, 3, 4, 5, 6) = 21 Для доступа к списку параметров используется указатель pik типа int *. Вначале ему присваивается адрес явно заданного параметра k, т.e. он устанавливается на начало списка параметров в памяти (в стеке). Затем в цикле указатель pik перемещается по адресам следующих фактических параметров, соответствующих неявным формальным параметрам. С помощью разыменования *pik выполняется выборка их значений. Параметром цикла суммирования служит аргумент k, значение которого уменьшается на 1 после каждой итерации и, наконец, становится нулевым. Особенность функции возможность работы только с целочисленными фактическими параметрами, так как указатель pik после обработки значения очередного параметра "перемещается вперед" на величину sizeof (int) и должен быть всегда установлен на начало следующего параметра. Следующий пример содержит функцию для вычисления произведения переменного количества параметров. Признаком окончания списка фактических параметров служит параметр с нулевым значением. //Программа 6.3 #include "stdafx.h" #include <iostream> // Функция вычисляет произведение параметров: double prod(double arg,...){ double aa = 1.0; // Формируемое произведение double* prt = &arg; /* Настроили указатель на первый параметр*/ if (*prt == 0.0) return 0.0; for(;*prt; prt++) aa *= *prt; return aa; } void main(){ double prod(double,...); // Прототип функции std::cout <<"\n prod(2., 4., 3., 0.0) =" <<prod(2.,4.,3., 0.0); std::cout <<"\n prod(1.5, 2.0, 3.0, 0.0) = "; std::cout<<prod(1.5, 2.0, 3.0,0.0); std::cout<<"\n prod(1.4, 3.0, 0.0, 16.0, 84.3, 0.0) = "; std::cout << prod(1.4, 3.0, 0.0, 16.0, 84.3, 0.0); std::cout << "\n prod (0.0) = " << prod (0.0); getchar(); } Результат выполнения программы: prod(2., 4., 3., 0.0) = 24 prod(1.5, 2.0, 3.0, 0.0) = 9 prod(1.4, 3.0, 0.0, 16.0, 84.3, 0.0) = 4.2 prod(0.0) = 0 В функции prod перемещение указателя prt по списку фактических параметров выполняется всегда за счет изменения prt на величину sizeof (double). Поэтому все фактические параметры при обращении к функции prod () должны иметь тип double. В вызовах функции проиллюстрированы некоторые варианты задания параметров. Обратите внимание на вариант с нулевым значением параметра в середине списка. Параметры вслед за этим значением игнорируются. Чтобы функция с переменным количеством параметров могла воспринимать параметры различных типов, необходимо в качестве исходных данных каким-то образом передавать ей информацию о типах параметров. Для однотипных параметров возможно, например, такое решение передавать с помощью дополнительного обязательного параметра признак типа параметров. В приведенных примерах функций с изменяемыми списками параметров перебор параметров выполнялся с использованием адресной арифметики и явным применением указателей нужных типов. К проиллюстрированному способу перехода от одного параметра к другому нужно относиться с осторожностью. Дело в том, что при обращении к функции ее параметры помещаются в стек, причем порядок их размещения в стеке зависит от реализации компилятора. Более того, в компиляторах имеются опции, позволяющие изменять последовательность помещения значений параметров в стек. Стандартная для языка Си++ последовательность размещения параметров в стеке предполагает, что первым обрабатывается и помещается в стек последний из параметров функции. При этом у него оказывается максимальный адрес (так стек устроен в реализациях на IBM PC). Противоположный порядок обработки и помещения в стек будет у функций, определенных и описанных с модификатором pascal. Этот модификатор и его антипод модификатор cdecl являются дополнительными ключевыми словами, определенными для компиляторов ТСи++ и ВСи++.
Рекурсивные функции Рассмотрим принципиальные возможности, которые предоставляет язык Си++ для организации рекурсивных алгоритмов. Предварительно отметим, что различают прямую и косвенную рекурсии. Функция называется косвенно рекурсивной в том случае, если она содержит обращение к другой функции, содержащей прямой или косвенный вызов определяемой (первой) функции. В этом случае по тексту определения функции ее рекурсивность (косвенная) может быть не видна. Если в теле функции явно используется вызов этой функции, то имеет место прямая рекурсия, т.е. функция, по определению, рекурсивная. Классический пример - функция для вычисления факториала неотрицательного целого числа. long fact(int k){ if (k < 0) return 0; if (k == 0) return 1; return k * fact(k-l); } Для отрицательного аргумента результат по определению факториала не существует. В этом случае функция возвратит нулевое значение. Для нулевого параметра функция возвращает значение 1, так как по определению, 0! равен 1. В противном случае вызывается та же функция с уменьшенным на 1 значением параметра и результат умножается на текущее значение параметра. Тем самым для положительного значения параметра k организуется вычисление произведения k * (k-l) * (k-2) *...*3*2*1*1 Обратите внимание, что последовательность рекурсивных обращений к функции fact прерывается только при вызове fact(0). Именно этот вызов приводит к последнему значению 1 в произведении, так как последнее выражение, из которого вызывается функция, имеет вид: 1 * fact(1-1) Поскольку в теле самой функции fact() присутствует её вызов, то по определению такую функцию называют прямой рекурсивной функцией. В качестве еще одного примера рекурсии рассмотрим функцию QuickSort(), реализующую алгоритм быстрой сортировки. //Программа 6.4 #include "stdafx.h" #include <stdio.h> #include <stdlib.h> #define DIMENSION 100 void QuickSort(int array[], int First, int Last) { int Temp, LowerBoundary, UpperBoundary, Separator; LowerBoundary = First; UpperBoundary = Last; Separator = array[(First + Last) / 2]; do{ while (array[LowerBoundary] < Separator) LowerBoundary++; while (array[UpperBoundary] > Separator) UpperBoundary--; if (LowerBoundary <= UpperBoundary){ Temp = array[LowerBoundary]; array[LowerBoundary++] = array[UpperBoundary]; array[UpperBoundary--] = Temp; } } while (LowerBoundary <= UpperBoundary); if (First<UpperBoundary)QuickSort(array,First,UpperBoundary); if (LowerBoundary<Last) QuickSort(array, LowerBoundary, Last); } void main() { int array[DIMENSION], i = 0; for (; i < DIMENSION; array[i++] = rand()%1000); QuickSort(array, 0, DIMENSION -1); printf("\n\n"); for (i = 0; i < DIMENSION; printf("\n%d", array[i++])); getchar(); }
|
||||
Последнее изменение этой страницы: 2016-08-10; просмотров: 192; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.145.75.238 (0.007 с.) |