Функции с переменным количеством параметров

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

Формат функции:

<тип_возвращаемого_значения> <имя_функции>

(<спецификация_обязательных_параметров>, …) { …. }

В таких функциях должен быть хотя бы один обязательный параметр.

Функция с переменным количеством параметров должна иметь при своем выполнении механизм определения количества параметров и их типов. Возможны два подхода:

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

- добавление в конец списка параметров параметра с уникальным значением.

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

Ниже представлены две функции с переменным количеством параметров, демонстрирующих два описанных выше подхода для определения числа реальных параметров.

#include <stdio.h>

int GetSum(int n,...) // Функция считает сумму параметров n - число параметров

{

  int *p=&n; // Настраиваем указатель на 1-ый параметр

  int sum=0; // Начальное значение суммы

  int i; // Индексная переменная

  for(i=0; i<n; i++) // Суммируем n параметров

        sum+=*++p; // Суммируем параметры (увеличиваем указатель на 1 и

                                                 // обращаемся по адресу)

  return sum;

}

 

double GetProiz(double par1,...) // Функция считает произведение параметров, признак окончания списка параметров значение 0.0

{

  double pr=1; // Начальное значение произведения

  double *p=&par1; // Настраиваем указатель на 1-ый параметр

  for(; *p; p++) pr*=*p; // Умножаем на очередной параметр пока не встретится 0

  return pr;

}

void main()

{

  printf("sum=%d", GetSum(5, 1, 2, 3, 4, 5)); // Будет напечатано sum=15

  printf("\nproiz=%f", GetProiz(2.0, 3.0, 4.0, 1.5, 0.0));

                                      // Будет напечатано proiz=36.000000

}

 

Рекурсивные функции

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

#include <stdio.h>

int fact(int a) // Рекурсивная функция для вычисления факториала

{

  if (a<0) return 0;

  if (a==0) return 1;

  return a*fact(a-1); // Вызов функции внутри себя

}

void main()

{

  printf("fact(5)=%d", fact(5)); // Печатается fact(5)=120

}

 

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

 

7.7. Подставляемые (inline) функции

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

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

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

• функция слишком велика;

• функция рекурсивная;

• обращение к функции идет до ее определения;

• функция вызывается более 1 раза в одном выражении;

• функция имеет цикл, переключатель или оператор goto.

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

#include <stdio.h>

#include <math.h>

inline double getVect(double x, double y)

// Функция считает длину вектора, заданного точкой x, y

{

return sqrt(x*x+y*y);

}

 

void main()

{

printf("%f", getVect(3., 4.));

 

 

Указатели на функции

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

Формат определения указателя на функцию:

<тип_возвращаемого_значения> (* <имя_указателя>) (<спецификация_формальных_параметров>);

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

Ниже представлен пример использования указателей на функции. В программе вводятся с клавиатуры два вещественных значения, знак операции (+, -, *, /), между значениями выполняется введенная операция, результат выводится на печать. В зависимости от введенного знака операции указатель на функцию в программе настраивается на одну из функций.

#include <stdio.h>

double summa(double x, double y) // Функция вычисляет сумму параметров

{

  return x+y;

}

 

double mult(double x, double y) // Функция вычисляет произведение параметров

{

  return x*y;

}

 

double div(double x, double y) // Функция вычисляет результат деления параметров

{

  return x/y;

}

 

double razn(double x, double y) // Функция вычисляет разность параметров

{

  return x-y;

}

 

void main()

{

  double x, y, Rez; // Два исходных параметра и результат операции

  char Ch; // Символ определяет знак выполняемой операции

  double (*pFun)(double, double); // Указатель на функцию

  printf("x="); scanf("%lf", &x); // Ввод x

  printf("y="); scanf("%lf", &y); // Ввод y

  printf("Ch="); fflush(stdin); // Сброс буфера ввода

  scanf("%c", &Ch); // Ввод знака операции с клавиатуры

  switch(Ch) // В зависимости от знака операции настраиваем указатель

                                                   // на одну из функций

  {

  case '+': pFun=summa; break;

  case '-': pFun=razn; break;

  case '*': pFun=mult; break;

  case '/': pFun=div; break;

  default: printf("Error!"); return;

  }

  Rez=(*pFun)(x, y); // Вызов функции через указатель

  printf("Rez=%f", Rez);

}                         

 

7.9. Перегрузка функций (Си++)

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

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

#include <stdio.h>

int summa(int a, int b) // Функция считает сумму 2-х параметров типа int

{

  return a+b;

}

 

int summa(int a, int b, int c) // Функция считает сумму 3-х параметров типа int

{

  return a+b+c;

}

 

double summa(double x, double y) // Функция считает сумму 2-х параметров типа double

{

  return x+y;

}

 

void main()

{

  int s1, s2;

  double s3;

  s1=summa(2, 3); // Вызывается первая функция

  s2=summa(5, 5, 1); // Вызывается вторая функция

  s3=summa(1., 2., 1.5); // Вызывается третья функция

  printf("s1=%d s2=%d s3=%f", s1, s2, s3);

}

 

Шаблоны функций

 

Шаблоны функций появились в языке Си++, в языке Си их не было.

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

Формат шаблона функции:

template <class имя1, class имя2, …, class имяN>

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

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

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

• имена параметров шаблона должны быть уникальными;

• список параметров не может быть пустым;

• перед каждым параметром идет ключевое слово class;

• все параметры шаблона должны быть обязательно использованы в спецификации формальных параметров функции;

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

• допустима перегрузка шаблонов.

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

template <class T1> // T1 – неизветсный тим элемента массива

T1 summa(T1 *p, int n) // p– указатель на массив, n– число элементов массива

{

T1 sum=0; // Начальное значение суммы 0

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

       sum+=p[i];

return sum;

}

 

int main(int argc, char* argv[])

{

int M[]={ 1, 2, 3, 4};

double X[]={1.5, 2, 3.4, 5};

printf("%d", summa(M, 4)); // По шаблону создается функция

                            // вместо T1 подставляется тип int

printf("\n%f", summa(X, 4)); // По шаблону создается функция

                            // вместо T1 подставляется тип double

return 0;

}


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: