шаблоны сайт визитка joomla
Скачать шаблоны Joomla 3.5 бесплатно
Вы находитесь здесь:Главная»Учебные материалы»C ++»C++. Функции и процедуры

C++. Функции и процедуры

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

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

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

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

Вызовом подпрограммы называется обращение к ней по имени с целью её использования. Вызов — это запуск подпрограммы.

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

Итак, конкретизируем понятие функции:

Функция — это определенная группа операций с уникальным именем, которая может:

  1. Вызываться по имени в любом месте программы.
  2. Получать определенный набор значений из внешней программы в момент вызова.
  3. Возвращать в качестве значения некоторый результат заранее заданного типа.

Также функцию можно рассматривать как операцию, созданную самим разработчиком.

Известный примером функции является main. Она автоматические вызывается при запуске программы.

Создание функции

До того, как функция будет вызвана, она должна быть объявлена.

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

Объявление функции называют также её прототипом.

Схема:

Тип_результата Имя_функции (Тип_пар1, Тип_пар2, ...);
  • Тип_результата — некоторый существующий (например, встроенный) тип данных или ключевое слово void,  указывающее на то что функция никакого значения возвращать не будет.
  • Имя_функции — уникальный для данного пространства имён идентификатор.
  • Тип_парN — некоторый существующий (например, встроенный) тип данных для N-oro аргумента.

Примеры:

int max (int, int);
double cube (double)
float massa();
void printarr(*int, int);

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

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

Определение (описание) функции

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

Схема:

Тип_результата Имя_функции (Тип_пар1 Имя_пар1, Тип_пар2 Имя_пар2, ...) {
  Оператор1;
  Оператор2;
  ...
  ОператорN;
  return n;
};
  • Имя_парN — уникальное внутри функции имя N-ro параметра. Имена параметров можно задавать и в прототипе функции, тогда в определении надо использовать те же имена.
  • ОператорN — некоторые операторы и выражения, содержащиеся внутри функции и выполняющиеся каждый раз при вызове функции. Внутри операторов мы можем обращаться к глобальным объектам программы; к локальным объектам, объявленным внутри функции; а также к аргументам функции.
  • return n — оператор, останавливающий работу функции и возвращающий n в качестве её значения (при этом тип n должен соответствовать типу результата в объявлении функции). Наличие этого оператора обязательно для функции возвращающей значение. Для функции объявленной как void можно вызывать оператор return без аргументов, это досрочно завершит функцию, иначе — будут выполнены все действия до конца блока описания функции.

Блок определения функции называется также её телом.

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

Пример объявления и описания функции:

int max (int, int);
int max (int n1,int n2) {
  if(nl > n2) {
    return n1;
  } else {
    return n2;
  }
}
int main(void) {
  int а = 100 - max(10,20);
  cout << a;
  return 0;
}

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

Пример:

double cube (double a) {
  return a*a*a;
}
int main(void) {
  double pi = 3.1415;
  cout << cube(pi);
  return 0;
}

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

Пример:

double cube (double);
int main(void) {
  double pi = 3.1415;
  cout << cube(pi);
  return 0;
}
double cube (double a) {
  return a*a*a;
}

Такая возможность позволяет создавать модульные программы, исходный код которых может храниться даже в разных файлах.

Формальные и фактические параметры

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

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

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

Пример:

int n = -25; // глобальная переменная
int modul (int n) { // n - формальный параметр
  if(n<0) n = -1 * n; // n будет перекрывать глобальную переменную с именем n
  return n;
}
int main(void) {
  cout << modul(n); // 25, значение глобальной переменной n будет передано в функцию
  cout << n; // -25, но работа внутри функции пойдёт с локальной переменной n
  return 0;
}

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

Очередность вызова и рекурсия

Одна функция может вызываться внутри другой.

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

Возможна и сложная (вложенная) рекурсия когда, например, функция А вызывает функцию В, а функция В — функцию А.

Пример простой рекурсии (функция вызывает саму себя):

int fib(int n) { 
  if(n == 1 || n == 2) {
    return 1;
  } else {
    return fib(n-1) + fib(n-2);
  }
}
int main(void) {
  cout << fib(10); // 55
  return 0;
}

Основной недостаток рекурсии — повторные вычисления одних и тех же значений.

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

Кроме того, в общем случае, в один момент времени может выполняться только одна функция, пока она не вернёт результат — не начнётся выполнение следующей функции. Управление от основной программы передаётся в функцию, пока функция не завершит свою работу, управление не вернётся к основной программе. В частности: ошибка в функции остановит всю программу.

Способы передачи параметров в функцию

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

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

  1. Из тела функции нельзя обратиться к какому-либо объекту, если он не является глобальным по отношению к функции или если его имя перекрыто одноимённой локальной переменной.
  2. При передаче больших объектов происходит их копирование и часто память расходуется напрасно.

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

В этих случая в функцию передаётся адрес объекта и, соответственно, работа внутри функции происходит не с копией, а с оригиналом объекта.

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

Пример:

void func1(int val, int& ref) {
   val++;
   ref++;
}
...
int a = 10, b = 10;
func1(a,b);
cout << a << endl; // 10, значение будет увеличено, но внутри функции, как локальное
cout << b << endl; // 11, будет увеличено значение внешней переменной b

При этом даже если имя формального и фактического параметра совпадут никакой проблемы не возникнет:

void pow2(int &a) {
    a = a*a; // тут не создаётся никаких объектов, кроме хранилища адреса
}
...
int a = 10;
pow2(a);
cout << a << endl; // 100

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

void decr(const int& n) {
   cout << n; // читать можно
   n--; // изменять нельзя, тут будет ошибка компиляции
}

В функцию можно передавать и указатели.

void prarr(int* a, const int& n) { // массив не копируется
    for(int i = 0; i<n; i++) {
        cout << a[i] << ' ';
    }
    *a = 100; // изменит первый элемент в глобальном массиве
    a++; // не вызовет ошибки, ведь указатель не константный
}
...
int a[] = {0, 5, 10, 15, 20};
prarr(a, 5); // 0 5 10 15 20
cout << *a << endl; // 100
a++; // вызовет ошибку

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

Массив можно было передать в функцию и по-другом. Пример прототипа:

void prarr(int a[], const int& n);

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

Корректнее параметр, по которому будет передаваться указатель на первый элемент массива, объявлять как константный:

void prarr(int* const a, const int& n);

Тогда внутри функции мы не могли бы сместить указатель на соседний элемент.

Параметры со значениями по умолчанию (необязательные параметры)

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

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

Пример:

int pow(const int& a, const int& p) {
    int res = 1;
    for(int i = 1; i<=p; i++) {
        res = res * a;
    }
    return res;
}

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

cout << pow(5,2);

Но мы могли бы объявить функцию с параметром по умолчанию таким образом:

int pow(const int& a, const int& p = 2) {
...
}

Теперь можно вызывать функцию не указывая последнего параметра:

cout << pow(5); // 25

Что равносильно вызову:

cout << pow(5,2); // 25

Или указывая его:

cout << pow(5,4); // 625

У функции может быть несколько параметров по умолчанию. Например, рассмотрим прототип функции, которая могла бы копировать из одной строки в другую начиная с i-го символа и заканчивая n-ым:

void stringcopy(char* const from, char* to, int i = 0, int n = 99);

Эту функцию можно вызывать с двумя, тремя или четырьмя параметрами. Если вызов будет с 2 параметрами, то скопируются первые 100 символов.

Если с тремя, то мы сможем указать с какого символа начать копирование, но продолжится оно до 100-го символа. Если укажем все четыре параметра, то сможем скопировать, например, символы с 30-го по 50-й.

Если мы укажем 3 параметра при вызове, то первые два значения будут отнесены к обязательным параметрам, а третье — к первому необязательному. Значения необязательным параметрам присваиваются в порядке их следования, слева направо. Нет возможности сохранив значение по умолчанию третьего параметра, как-то передать значения последнему.

Новости о нас

КАЖДУЮ ПЯТНИЦУ - бесплатное занятие по 3D-моделированию с 16:40 до 18:10. Только для учащихся нашего технопарка! Подробнее
 Региональный отборочный фестиваль "Робофест-Стерлитамак 2018" состоится 25-26 января в г.... Подробнее
В сезоне 2017/18 фестиваля «РобоФест Стерлитамак 2018» Региональный учебно-тренировочный сбор... Подробнее
ВНИМАНИЕ учащимся! Объявляем конкурс рисунков на тему "Робот моей мечты", который пройдет по двум... Подробнее
В нашем технопарке Толтек СФ БашГУ пройдут открытые ОТБОРОЧНЫЕ СОРЕВНОВАНИЯ для ВСЕХ возрастов и... Подробнее

Наши партнеры

 TZOyoOCZ8y0 logotip novy SF BashGU  utv logo  CityMoll  VolnoeDelo