Возврат Значения (оператор return)

Из функции, которая не описана как void, можно (и должно) возвращать значение. Возвращаемое значение задается оператором return.

Например:

int fac(int n) {return (n>1) ? n*fac(n-1) : 1; }

В функции может быть больше одного оператора return:

int fac(int n)

{

if (n > 1)

return n*fac(n-1);

else

return 1;

}

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

Например:

double f()

{

// ...

return 1; // неявно преобразуется к double(1)

}

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

int* f() {

int local = 1;

// ...

return &local; // так не делайте

}

Эта ошибка менее обычна, чем эквивалентная ошибка при использовании ссылок:

int& f() {

int local = 1;

// ...

return local; // так не делайте

}

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

Вот другой пример:

int& f() { return 1;} // так не делайте

Активизация функции

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

Например:

void error(char* p) { /* ... */ }

void (*efct)(char*); // указатель на функцию

void f()

{

efct = &error; // efct указывает на error

(*efct)("error"); // вызов error через efct

}

Чтобы вызвать функцию через указатель, например, efct, надо сначала этот указатель разыменовать, *efct. Поскольку операция вызова функции () имеет более высокий приоритет, чем операция разыменования *, то нельзя писать просто *efct("error"). Это означает *efct("error"), а это ошибка в типе. То же относится и к синтаксису описаний .

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

Например:

void (*pf)(char*); // указатель на void(char*)

void f1(char*); // void(char*)

int f2(char*); // int(char*)

void f3(int*); // void(int*)

void f()

{

pf = &f1; // ok

pf = &f2; // ошибка: не подходит возвращаемый тип

pf = &f3; // ошибка: не подходит тип параметра

(*pf)("asdf"); // ok

(*pf)(1); // ошибка: не подходит тип параметра

int i = (*pf)("qwer"); // ошибка: void присваивается int"у

}

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

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

Например:

typedef int (*SIG_TYP)(); // из

typedef void (*SIG_ARG_TYP);

SIG_TYP signal(int,SIG_ARG_TYP);

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

typedef void (*PF)();

PF edit_ops[] = { // операции редактирования

cut, paste, snarf, search

};

PF file_ops[] = { // управление файлом

open, reshape, close, write

};

Затем определяем и инициализируем указатели, определяющие действия, выбранные в меню, которое связано с кнопками (button) мыши:

PF* button2 = edit_ops;

PF* button3 = file_ops;

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

(button2[3])();

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

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

typedef int (*CFT)(char*,char*);

int sort(char* base, unsigned n, int sz, CFT cmp)

{

for (int i=0; iname, Puser(q)->name);

}

int cmp2(char*p, char* q) // Сравнивает числа dept

{

return Puser(p)->dept-Puser(q)->dept;

}

Эта программа сортирует и печатает:

main ()

{

sort((char*)heads,6,sizeof(user),cmp1);

print_id(heads,6); // в алфавитном порядке

cout << "n";

sort((char*)heads,6,sizeof(user),cmp2);

print_id(heads,6); // по порядку подразделений

}

Можно взять адрес inline-функции, как, впрочем, и адрес перегруженной функции.


ПРАКТИЧЕСКАЯ ЧАСТЬ

Задание: Элемент матрицы называется локальным минимумом, если он строго меньше всех имеющихся у него соседей. Подсчитать количество локальных минимумов заданной матрицы размером 10х10.

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

 
 


-
+
-
-
+
+
Элемент меньше всех соседних элементов
Инкрементируем счетчик
Задаем массив

Текст программы:

#include <vcl.h>

#include <stdio.h>

#include <stdlib.h>

#include <time.h>

#pragma hdrstop

#include <iostream>

#include <iomanip>

using namespace std;

 

#pragma argsused

 

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

{int A[10][10],i,k,z=0,n=1; //описываем переменные

 

srand(time(NULL));

for(i=0;i<10;i++) //цикл для создания массива

{ cout<<endl;

for(k=0;k<10;k++)

{A[i][k]=(50-(rand()%70));

cout<<setw(4)<<A[i][k];

}}

cout<<endl<<endl;

for(i=0;i<10;i++) // цикл для сравнения элементов массива

{

for(k=0;k<10;k++)

{ if ((A[i][k]<A[i-1][k-1])&&(A[i][k]<A[i-1][k])&&(A[i][k]<A[i-1][k+1])&&(A[i][k]<A[i][k+1])&&(A[i][k]<A[i+1][k+1])&&(A[i][k]<A[i+1][k])&&(A[i][k]<A[i+1][k-1])&&(A[i][k]<A[i][k-1])) // сравнение элементов

{z=z++;

cout<<"Lokalniy minimum #"<<n<<": "; //вывод значений на экран

n=n++;

cout<<A[i][k]<<endl;}}}

cout<<endl<<"Kol-vo lokalnih minimumov: "<<z<<endl;

system ("pause");

return 0;

}

Рисунок 1 – Окно программы.

Заключение

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

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


Список литературы

1. Язык программирования С++. Вводный курс. Четвёртое издание. [Вильямс, 2007] (Стенли Липпман, Жози Лажойе)

2. Прототипы функций в С++[http://cppstudio.com/uchebniki/yazyk-programmirovaniya-s/prototipy-funkcij-v-s/]

3. Функции в C++[http://code-live.ru/post/cpp-functions/]