Партнерка на США и Канаду по недвижимости, выплаты в крипто
- 30% recurring commission
- Выплаты в USDT
- Вывод каждую неделю
- Комиссия до 5 лет за каждого referral
Функции
Функция – это основная программная единица языка СИ++.
Функции – это относительно самостоятельные фрагменты программы, оформленные особам образом и снабженные именем.
Имя функции – это имя константного указателя, значением которого является адрес участка оперативной памяти (ОП), в котором располагается программный код операторов функции.
Программа состоит из произвольного количества функций, одна (и единственная) из которых обязательно - главная функция с именем main. Выполнение программы начинается с выполнения функции main , выполнение неглавных функций инициируется в главной функции непосредственно или в других функциях, которые инициированы в главной.
Функции по умолчанию имеют класс памяти – extern ( т. е. глобальны ), доступны во всех модулях программы.
Функция должна быть определена или по крайней мере описана (дан прототип функции) до обращения к ней.
Функции, определенные в других файлах программы, для использования в данном файле должна быть описана в данном файле со спецификатором extern.
Для того чтобы функция использовалась только в том файле, в котором она определена, надо чтобы в определение входил спецификатор static.
Каждая функция существует в программе в единственном экземпляре, в то время как обращаться к ней можно многократно из разных точек программы.
Упоминание имени функции в тексте программы называется вызовом функции. При вызове функции активизируется последовательность образующих ее операторов, а с помощью передаваемых функции параметров осуществляется обмен данными между функцией и вызывающей ее программой.
Определение функции
Определение функции – это программный текст функции. Определение функции может располагаться в любой части программы, кроме как внутри других функций.
В Языке Си++ нет вложенных функций!
Определение состоит из заголовка и тела функции:
<тип > <имя_функции > (<спецификация формальных
параметров>)
{ тело функции }
здесь:
1) тип – тип возвращаемого функцией значения, с помощью оператора return, если функция не возвращает никакого значения, на место типа следует поместить слово void;
2) имя функции – идентификатор, уникальный в программе;
3) список формальных параметров (сигнатура параметров) – заключенный в круглые скобки список спецификаций отдельных формальных параметров перечисляемых через запятую:
<тип параметра > < имя параметра >,
<тип параметра> < имя параметра >= <умалчиваемое
значение> ,
· если параметры отсутствуют, в заголовке после имени функции должны стоять либо пустые скобки - ( ), либо скобки со словом void – (void);
· для формального параметра может быть задано, а может и отсутствовать умалчиваемое значение – начальное значение параметра;
· может отсутствовать имя параметра, если последние не используются в теле функции, такой метод используется для резервации места в списке формальных параметров.
4) тело функции – это блок или составной оператор, т. е. последовательность определений, описаний и операторов, заключенная в фигурные скобки.
Пример определения функции:
float func ( float A[ ], int n, float*p , int& s )
{ тело функции }
где объявлены формальные параметры:
A – имя одномерного массива элементов типа float;
n - имя скалярной переменной целого типа;
p - имя указателя на переменную типа float;
s - имя ссылки на переменную типа int;
функция возвращает с помощью оператора return значение типа float.
Переменные, доступные функции
Переменные, доступные функции могут быть объявлены внутри или вне функции:
1) локальные переменные
- объявлены в теле функции, доступны и используются только в теле функции;
- при определении переменной ей выделяется память в сегменте стека, при завершении выполнения функции память освобождается;
- вне тела функции локальные переменные не существуют;
- обладают классом памяти avto (автоматические переменные);
2) формальные параметры
- объявлены в заголовке функции и доступны только функции;
- формальные параметры за исключением параметров – ссылок являются локальными переменными, память им выделяется в стеке;
- параметр – ссылка доступен только функции, но он не является переменной, на него не выделяется память, это некоторая абстракция для обозначения внешней по отношению к функции переменной;
3) глобальные переменные
- переменные объявлены в программе как внешние, т. е. вне всех функций, включая и главную функцию main;
- область действия таких переменных – вся программа от точки объявления переменной;
- память внешним переменным выделяется на этапе компиляции программы в сегменте данных, и не освобождается до конца программы;
- чтобы глобальная переменная была доступна функции, функция не должна содержать локальных переменных и формальных параметров с тем же именем; локальное имя «закрывает» глобальное и делает его не доступным;
Список формальных параметров функции указывает, с какими аргументами или фактическими параметрами следует вызывать функцию.
Имена формальных параметров используются в теле функции - это локальные переменные функции, им выделяется память в стеке.
Фактические параметры передаются в функцию при вызове, заменяя формальные параметры. И копии фактических параметров или сами фактические параметры обрабатываются функцией.
Фактические параметры, по количеству должны, по типу ( он должен быть идентичным или совместимым), по расположению должны соответствовать формальным параметрам.
тело функции – это блок ( последовательность определений, описаний и операторов, заключенная в фигурные скобки)
Оператор return
- очень важный для функции оператор возврата управления программой и значения в точку вызова функции.
return выражение – определяет возвращаемое функцией значение
(значение выражения)
return – если функция не возвращает результата или return
может отсутствовать
( return (x*x) ; return (*s) ; return (0) ; return ; )
Функция завершается как только встречается оператор return , если функция возвращает результат. Если функция не возвращает результата, она завершается по окончанию тела функции.
Если функция возвращает результат, то ее можно использовать в выражениях, напр. в правой части оператора присваивания, или в списке фактических параметров другой функции, везде, где допустимо значение. (Аналог функций Паскаля).
Однако такие функции можно вызывать и как операторы, чтобы они не возвращали значения, а производили действия, также описанные в теле функции.
Если функция не возвращает результата, типа void, вызов функции нельзя использовать в выражениях, вызов – это оператор.
Пример:
Функции, возвращающие результат с помощью return могут использоваться в выражениях и могут вызываться как операторы:
i = fscanf ( f, “%d %f ” , &a, &b ) ; -
- fscanf возвращает с помощью return количество значений, введенных и присвоенных переменным списка данных.
fscanf ( f, “ % d %f , &a, &b ) ; - оператор
результат работы fscanf – это введение значений и присвоение их
переменным списка данных
Примеры объявлений функций : 1. int add ( int x, int y )
{ return ( x+y) }
void main ( )
{ printf ( “%d\n” , add(4, 8)) ;
// cout<<add(5,9); }
2. int max ( int a, int b)
{ if ( a> b ) return a ;
return b;}
Прототип:
При вызове функции формальные параметры заменяются на фактические, причем соблюдается строгое соответствие параметров по типам на этапе компиляции. До обращения к функции должно быть либо полное определение функции или ее описание - прототип с указанием типов параметров:
<Тип функции > < имя функции>
( <спецификация формальных параметров>) ;
Отличие от определения функции:
1) наличие ‘ ; ‘
2) необязательность имен параметров
int max (int, int ) ;
Прототипы стандартных библиотечных функций хранятся в заголовочных файлах, которые необходимо включать в программу с помощью препроцессорных команд
#include <имя файла>
#include <iostream. h>
Прототипы функций и описания внешних объектов пользователя можно помещать в отдельный файл, который включают в программу командой:
#include “ имя файла “
Переменные функции
(должны быть объявлены внутри или вне функции)
4) глобальные –должны быть объявлены в файле как внешние и не переобъявлены в данной функции;
5) локальные - объявлены, доступны и используются только в теле функции;
6) формальные параметры-объявлены в заголовке функции и доступны только функции ( те же локальные переменные)
Вызов функции
( операция круглые скобки, если функция возвращает значение)
Имя функции ( список фактических параметров);
Соответствие формальных и фактических параметров должно быть по количеству, типу, и по расположению формальных параметров.
В общем случае фактический параметр(аргумент ) – это выражение.
Если функция без параметров :
Имя функции ( );
При вызове функции происходит передача фактических параметров в функцию и передача управления в функцию.
После завершения выполнения всех операторов функция возвращает управление и результат.
Передача фактических параметров
Может быть по значению, по адресу, по ссылке
1) По значению.
Формальным параметром может быть только имя скалярной переменной стандартного типа или структуры, определенной пользователем.
( Массивы, функции, файлы передаются по указателю –адресу.!)
При определении функции формальному параметру выделяется память в стеке.
Фактическим параметром является –выражение, значение которого копируется в стек, в область, выделенную под формальный параметр.
Все изменения, происходящие с формальным параметром - значением
в теле функции существуют только в функции и не передаютя переменной, значение которой являлось фактическим параметром функции.
. . .
void R ( int a, int b, int c)
{ cout << a<<b<<c<<endl ;
a+=100; b+=100; c+=100
cout << a<<b<<c<<endl ; }
void main ( ) {
int a1=1, b1=2, c1=3;
R ( a1, b1, c1 ) ; // аналогично было вызвать так R(1, 2 , 3) ,
// т. к. нас интересуют только значения
// фактических параметров
cout << a1<<b1<<c1<<endl ;
}
1 2 3
1 2 3 // значения фактических параметров
// функция не меняет

стек

a1 b1 c1 a b c адреса
![]()

![]()
стек

![]()

![]()
3
a1 b1 c1 a b c адреса
2)По адресу ( по указателю).
Формальным параметром является указатель type*p .
Фактическим параметром должно быть значение для указателя.
Это может бать либо адрес переменной типа type, либо значение другого указателя, типа type из вызывающей программы.
Т. е. в стек в область памяти, выделенную для p будет копироваться
значение некоторого адреса из вызывающей функции.
В теле функции, используя операцию разыменования *p, можно изменять содержимое участка памяти на который указывает p.
Адрес переменной, или значение какого либо указателя из вызывающей функции является значением фактического параметра и копируется в стеке, при этом, сам адрес не меняется при всех изменениях происходящих в функции, т. е. при изменении формального параметра, фактический не меняется, но можно изменить содержимое участка с данным адресом, используя операцию разыменования, т. е. по этому адресу передаются все изменения происходящие с *p.
Все обращения к * p в функции, равносильны при вызове функции обращению к объекту, адрес которого и передается в функцию в качестве фактического параметра при вызове функции.
Таким образом изменения происходимые в функции с *p будут происходить по адресу некоторой внешней переменной, адрес которой передается в функцию при вызове в качестве фактического параметра.
Фактическим параметром передается указатель – адрес переменной того же типа ( скаляра, массива, структуры, массива структур, файла, массива файлов, массива указателей или указатель функции)
. . .
void RR ( int* a, int* b, int*c)
{ cout << * a <<*b << * c ;
* a+=100; *b+=100 ; * c+= 100 ;
cout << *a << *b << *c ; }
void main ( )
{ int a1 =1 , b1= 2 , c1=3 ;
RR ( &a1 , &b1 , & c1 );
cout << a1 << b1 <<c1 ; }
1 2 3
![]()
стек
1 2 3 &a1 &b1 &c1
a1 b1 c1 a b c адреса

стек
*a+=100
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
*b+=…
![]()
*c+=… &a1 &b1 &c1
a1 b1 c1 a b c адреса
2) по ссылке
C помощью ссылки создается второе имя переменной.
int b ;
int &a=b ; a - синоним b
Для ссылки а не выделяется память при ее объявлении, имя а -это другое имя участка памяти, выделенного под переменную b (инициализатор).
Все операции, производимые над ссылкой (a), производятся над основной переменной (b).
Объявление ссылки отличается от операции взятия адреса обязательностью указанием типа перед аперсандом int & .
Если ввести ссылку ( int & a) и не инициировать ее переменной то возникает объект который имеет имя, но он не связан ни с каким участком памяти
Если в качестве формального параметра взять - неинициированную ссылку, а в качестве фактического параметра – имя переменной,
то фактический параметр инициирует ссылку, связывает ее с участком памяти, выделенному под фактический параметр.
Таким образом обеспечивается доступ из функции к участку памяти, выделенному под фактический параметр и все изменения происходящие в функции с ссылкой будут происходить непосредственно с переменной, являющейся фактическим параметром.
. . . void RRR ( int & a, int &b, int & c )
{ cout << a << b <<c ;
a += 100; b+= 100; c+= 100; }
cout << a <<b <<c ; }
void main ( )
{ int a1=1 , b1 =2 , c1=3;
RRR( a1, b1 , c1 ); 1 2 3

cout << a1 <<b1 << c1 ; }
стек

![]()
1 2 3
a1 b1 c1
a b c
Фактическим праметром может быть также имя другой ссылки, инициированной в вызывающей функции.
Классификация формальных параметров
( Скаляр – значение переменной основного типа, элемент массива или структуры)
1) формальные параметры :
а) скаляры,
б) указатели на скаляры
в) ссылки на скаляры
фактические параметры :
а) значения скаляров,
б) адреса скаляров
в) имена скаляров( имя участка памяти);
2) формальные параметры : массивы или указатели на массивы
фактические параметры : имя массивов или имена указателей на массив;
3) формальный параметр :
а) определение структуры,
б) указатель на структуру или массив структур
в) ссылка на структуру;
фактический параметр :
а) имя структуры(копирование структуры) ,
б) адрес ( указатель) структуры или указатель на массив структур,
в) имя структуры ( работа со структурой без копирования, т. е имя участка памяти, выделенного под структуру);
4) формальный параметр – указатель на функцию
фактический параметр – имя функции
1) Формальный параметр – скаляр, указатель на скаляр, ссылка на скаляр
Примеры были рассмотрены выше при рассмотрении передачи параметров по значению, по адресу и по ссылке.
Пример
1. #include<iostream. h>
void mult ( int a, int b, int *s )
{ * s = a * b }
void main ( )
{ int c=3, d=7 , s;
mult ( c, d, & s) // mult ( 3, 7 , & s)
cout<< s }
2. #include <stdlib. h>
#include <time. h>
#include<iostream. h>
void minmax ( int a[n], int & min, int & max )
{ min = max = a[0];
for ( int i =0 ; i< n ; i++)
if ( a[i] < min ) min = a[i];
else if ( a[i] > max ) max = a[i]; }
void main ( )
{ int a[n] , min, max ;
randomize ( )
for ( int i=0 ; i< n ; i++)
a[i] =rand() ;
minmax ( a, min, max );
cout << min << max ; }
2) Формальные параметры – массивы
Одномерный массив
int a[m] эквивалентно int * p =a // стат. выд. памяти
int*p=new int [m]; // динамическое
обращение элементам :
a[i] , *(a+ i ) p[i] , * (p+ i ) , *p++
а = = & a = = & a[0] – адрес нулевого элемента
*a = = a[0] = = *&a[0] – значение нулевого элемента
Многомерные массивы:
int a[m][n]
a[i][j] - значение элемента i-й строки, j-го столбца
a[i] - адрес начала ( первого элемента ) i - ой строки
a – адрес массива (нулевого элемента) массив
a[0] = = *(a +0) == *a = = &a[0][0] - адрес начала нулевой строки:
адрес нулевого элемента массива
**a= = * a[0] = = * (& a[0][0]) = = a[0][0] - значение нулевого
элемента массива
a[i] = = *(a+i) = = & a[i][0] - адрес начала i –ой строки : адрес
нулевого элемента i –ой строки
*a[i] == **(a+i) = = * (&a[i][0] ) = =a [i] [0] – значение нулевого
элемента i –ой строки
a[i][j] = =*(*(a + i ) + j ) = = *( a[i] + j ) - значение j-го элемента
i – ой строки
( *(a+i) + j) = = & a[i][j] - адрес j-го элемента i-ой строки
I) Формальный параметр - определение массива:
1) Определение массива с фиксированными границами;
int a[5] ; float b [2][3][4] ;
2) Определение массива с пустой первой границей, и параметр для размера границы
int a [ ] ; float b[ ][3] [4] ; и int n;
Фактический параметр :
1) имя массива;
2) имя массива и размер левой границы
Пример 1.
Функция, возвращающая суммы элементов двумерного массива
. . . float S ( float a [ ] [n] , int m )
{ float s = 0;
for ( int i=0 ; i< m ; i++)
for( int j=0 ; i< n ; j++ ) s+= a[i][j];
return s ; }
void main ( )
{ float dat [][5] = { { } , { } };
cout << S (dat, 2) ; }
2. Функция, заполняющая трехмерный массив и возвращающая
сумму элементов
. . . float S ( float a [ ] [n][k] , int m )
{ float s = 0; int l =1;
for ( int i=0 ; i< m ; i++)
for( int j=0 ; i< n ; j++ )
for ( int t=0 ; t< k ; t++)
{ a[i][j][t]=l++ ; // *(*(*(a+i) + j) + t)
s+= a[i][j][t]; }
return s ; }
void main ( )
{ float dat [ ][4][5] = {{ {…..},{…..} ,{ …..}, {…..} } ,
{ {…..},{…..} ,{ …..}, {…..} } }
cout << S (dat, 2) ; }
II) Формальный параметр - указатель :
указатель на начало массива ;
int * a ; float *d;
а) одномерный массив
формальный параметр: type * a –указатель на начало массива
(type-тип элементов массива)
нужен второй параметр – размер массива.
фактический параметр: имя массива или имя указателя –
переменной на конкретный массив статической памяти или на
динамический массив
Пример 1. Функция, формирующая упорядоченный массив из
элементов двух других массивов
int Q ( int * a, int * b, int *c, int n, int m )
{ int i, j, p ;
for ( i=0 , j =0 ; i< n+m ; i++ , j++ ) // формирование
if (i < n ) c[j] = a[i];
else c[j] = b[ i-n] ;
for ( i =0 ; i < n+m ; i++ ) // упорядочивание
for ( j = n+m – 1 ; j < i ; j-- )
if ( с[j] < c[j-1] ) p= c[j] ; c[j] = c[j-1] ; c[j-1] = p ;
}
void main ( )
{ int i, a [ ] = { } , b[ ] = { } ;
n= sizeof( a ) / sizeof ( a[0]);
m= sizeof( b ) / sizeof ( b[0]) ;
int c [ n+m] ; // выделена статически память под
// массив
Q (a, b, c, n, m ) ;
for ( i =0 ; i < n+m ; i++ ) cout << c[i] << “ “ ;}
с динамическим массивом :
void main ( )
{ int i, a [ ] = { } , b[ ] = { } ;
n= sizeof( a ) / sizeof ( a[0]);
m= sizeof( b ) / sizeof ( b[0]) ;
int* x = new int [ n+ m ] // выделена динамически память
// под результирующий массив
Q (a , b , x , n , m ) ;
for ( i =0 ; i < n+m ; i++ ) cout << x[i] << “ “ ; delete [ ] x ;
б) Формальный параметр - указатели на двумерный массив
( type a[m][n] )
формальные параметры:
1) type * a -указатель на начало массива (на нулевой элемент массива);
фактический параметр- адрес первого элемента массива
2) type * p [ m] – массив из m указателей на m строк двумерного
массива, длиной n элементов;
фактический параметр- имя массива указателей на строки матрицы
3) type ** pp - указатель на массив указателей на строки
для динамического выделения памяти ;
фактический параметр - имя массива указателей на строки
или имя указателя на указатель из вызывающей
функции
1) Пример 1. Функция нахождения суммы элементов матрицы
float sm ( float * a, int n, int m ) // a - указатель на
{ // нулевой элемент массива
float s =0.0 ; int i ;
for (i =0 ; i < ( n * m ) ; i++) s += * a++ ; return s ;
}
void main ( )
{ float c[ ] [ 6] = { { } , { } } ;
cout << sm ( *c, 2 , 6 ) // *c - адрес нулевого элемента массива
}
2). Функция, возвращающая первое отрицательное число массива или нуль, если отрицательных чисел нет.
( здесь p – массив указателей на строки, n x m -размеры матрицы)
float neg ( int n, int m, float * p[ ] )
{ int i, j ;
for ( i=0 ; i < n ; i++)
for ( j=0 ; j < m ; j++) if ( p[i][j] < 0 ) return ( p[i][j]) ;
return (0); }
void main ( )
{ float c[5][12] = { { }, { } … { } };
float* prt [ ] = { *c, *(c+1), … *(c+n)} ;// статическое выделение
// памяти
cout << neg( 5,12,ptr ); }
“полудинамическое” выделение памяти
…void main() {
float* ptr[5];
for(int i=0;i<5; i++)
ptr[i]=new float [12];
for(int i=0;i<5; i++)
for(int j=0;j<12; j++) ptr[i][j] =rand();
cout<<neg(5,12,ptr)
3) Одномерный массив можно представить с помощью указателя
int c[ ] = { , , }; int* p = c ;
Аналогично и одномерный массив указателей
int * p [ ] = { } ; int* *ptr = p ; ( здесь int* - тип элементов массива)
Если надо сформировать массив в функции, для него надо выделить память либо статически, либо динамически.
Пусть функция возвращает результат - массив, используя формальный параметр) :
Т к. массив, сформированный в функции будет передаваться в main через формальный параметр, то выделение памяти под этот массив должно производится только в main ( статически или динамически).
void mas ( float ** ptr, int m, int n)
//void mas1 ( float * p[ ] , int m, int n ) – аналогично
{ randomize ( );
for ( int i =0 ;i<m ; i++ )
for ( int j =0 ; j < n; j++)
ptr[i][j] = rand( );
// p [i][j] = rand ( );
}
void main ( )
1. { float c [m][n] ; // статическое выделение памяти
float * p [m] = { * (c+0) , . . ., *(c+m) } ;
mas ( p, 5,6); // аналогично mas1(p, 5.6) ;
}
2. { float * p[m] ; // статическое выделение памяти
//на массив указателей
for( i=0 ; i< m ; i++ ) // динамическое выделение памяти для
p[i] = new float [n] ; //строк массива
mas1 ( p,5,6) ; . . . for( i=0 ; i< m ; i++ ) delete p[i]; }
3. { float **ptr;
ptr = new float * [ m]; // динамическое выделение
//памяти на массив указателей
for( i=0 ; i< m ; i++ ) // динамическое выделение памяти для
ptr[i] = new float [n] ; //строк массива
mas ( ptr,5,6) ; …
for( i=0 ; i< m ; i++ ) delete ptr[i];
delete [ ] ptr; }
в) Формальные параметры –указатели для представления
многомерных массивов
1)Формальный параметр – указатель на элемент многомерного массива
фактический параметр – адрес первого элемента массива
void vivod ( float * b , int n , int m ) //n-количество элементов массива
//вывод массива будет производится
//по m значений в строке
{ for ( int i=0 ; i < n ; i++ )
{ cout<<setw(6) << *b++ ; if (( i +1 ) % m = =0) cout << “\n”; }
}
void main ()
{ float c[ 7] = { }; // c – адрес нулевого элемента
float a [2][3] = { { }, { } } ; // *а – адрес нулевого элемента
float d [2][4][5] = { { { } , { } ,{ }, { } } , // **d- адрес нулевого
{ { } , { } ,{ }, { } } }; // элемента
vivod ( c,7 , 7) ; vivod ( *a, 6 , 3) ; vivod ( **d, 40 , 5) }
г) Формальный параметр - указатель на подмассив
<тип элемента> ( * имя указателя) [N1][ N2] [N3]…[NN]-
это указатель на массив [N1][N2]…[NN] с элементами тип.
int (*pt) [2][4] ; pt – указатель на объекты в виде двумерных
массивов 2х4 с элементами int
Операция new тип массива
1) выделяет в динамической памяти участок для размещения массива,
2) не позволяет его инициализировать.
3) Результат операции – адрес выделенного участка памяти на первый элемент массива
Например, для трехмерного массива (3х2х4) выделяется участок на
три матрицы 2х4 .Указателю на матрицу 2х4 присваивается адрес
участка выделенного под три матрицы-адрес первой матрицы.
float (* ptr ) [2][4] ; //определен указатель на матрицу
ptr = new float [3][2][4] ; // выделена память для массива из
//трех матриц
delete [ ] ptr ; / / освободит всю память, выделенную
// под массив
ptr[i][j][k]- обращение к элементам данного трехмерного массива
формальный параметр - указатель на подмассив и второй параметр количество таких подмассивов, необходимое для представления основного массива
фактический параметр- имя указателя на подмассив и количество подмассивов.
Пример : Функция заполняющая трехмерный массив натуральными
числами
void mas ( int ( * lp) [ 3][4], int n )
{ int i, j, k, t=1;
for ( i=0 ; i < n ; i++ ) {cout << “ \n\n”;
for ( j=0 ; j < 3 ; j++ ) { cout << “ \n”;
for ( k=0 ; k< 4; k++) { lp[i][j][k]= t++ ;
cout <<lp [i][j][k] <<” “ ; } } }
}
void main ()
{ int n; cin >> n; // вводится количество матриц
int ( *tp) [3][4] ; // определен указатель на матрицу
tp = new int [ n ] [3][4] ; // выделена динамическая память
// на n матриц, т. е. на трехмерный массив, и адрес
//первой матрицы присвоен указателю на матрицу tp.
mas ( tp, n ); . . .
delete [ ] tp ; }
Результат работы функции, возвращаемый с помощью оператора
Return
1) скалярное значение любого типа // return (5)
2) указатель на скаляр, массив, структуру, файл или функцию
2.1 Указатель на скаляр - возвращает адрес скаляра
Пример : Функция поиска в массиве числа
int * poisk ( int*c, int m, int n ) // c – указатель на элемент массив
// m – количество элементов массива , n- искомое число
{ int i, j
for ( i =0 ; i < m ; i++ )
{ if ( *c = = n ) return ( c ) ; c++ ; }
return (NULL);
}
void main ( )
{ int a[2][4] = { } , i, j ;
int c[6] = { } ;
int*p;
p= poisk ( *a, 8 ,7 );
if (p== NULL) cout << “ число отсутствует”;
else { cout<<(i= (p - *a ) / 4) ; cout<<( j = p - *a – i * 4) ; };
p = poisk ( c,6,7);
if ( p = = NULL) cout << “ число отсутствует”;
else i= p – c ;
}
2.2 Результат – указатель на одномерный массив.
Формирование динамических одномерных массивов происходит в функции!
Пример: Функция формирования одномерного динамического массива
Формальный параметр – размеры массива.
int * vvod ( int n)
{ int* p ; // указатель на формирующийся массив
p = new int [n] ; // выделение динамической памяти
for ( int i=0 ; i < n ; i++ )
cin >> p[i] ; return p }
void main ( )
{ int * dat , n ; // указатель на массив и
cin >> n; //количество элементов
dat = vvod (n) ;
for ( int i=0 ; i < n ; i++ )
cout << dat[i]<<’\t ‘;
. . . delete dat ; }
2.3 Результат – указатель на двумерный массив.
Формирование динамических двумерных массивов происходит в функции!
Пример : Функция, возвращающая двумерный массив
натуральных чисел
int** mas ( int m, int n )
{ int k=1;
int ** ptr ; // указатель на массив указателей на строки
// формирующегося массива
ptr = new int* [m ] // выделение динамической памяти
// под массив из m указателей
for ( int i=0 ; i < m ; i++ )
ptr [i] = new int [ n] // выделяется память для каждой строки
// формирующегося массива
for ( int i =0 ; i < m ; i++ ) { cout <<”\n” ;
for ( int j =0 ; j < n; j++ )
{ ptr [ i] [j] = k++ ; cout << ptr [i][j] << “ “ ; } } return ptr ;
}
void main ( )
{ int n, m, i ;
cin>> n>>m;
int ** Q; // определен указатель для формируемой матрицы
Q = mas ( m, n ) ; . . . Q[i][j]…
for ( i=0 ; i < m ; i++ )
delete Q[i] ;
delete [ ] Q; }
2.4 Результат указатель на подмассив:
Функция возвращает с помощью указателя на подмассив трехмерный массив.
#include <iostream. h>
typedef int (* TPM)[ 4][5] ; // TPM - тип указателя на матрицу 4х5
TPM fun (int n)
{ int i, j, k, t=1;
TPM lp=new int [n][4][5];
for ( i=0 ; i < n ; i++ ) {cout << " \n\n";
for ( j=0 ; j < 4 ; j++ ) { cout << " \n";
for ( k=0 ; k< 5; k++) { lp[i][j][k]= t++ ;
cout <<lp [i][j][k] <<" " ; } } }
return lp;
}
void main ()
{int n; cin>>n;
TPM D=fun( n);
delete []D;
}
Указатели на функции
Имя функции (без параметров и без типа результата) является указателем - константой на эту функцию. Значением этого указателя является адрес размещения кодов операторов функции в ОП. Это значение адреса можно присвоить другому указателю-переменной на функцию с тем же типом результата и с той же сигнатурой параметров. И затем этот указатель можно применять для вызова функции
Определение указателя – переменной на функцию:
< Тип функции> (* имя указателя) (спецификация
параметров) = < имя инициирующей функции>
Инициализация не обязательна, тип результата, сигнатура инициирующей функции должна полностью соответствовать указателю:
int * ( * fptr ) ( char * , int );
int (*ptr) (char*);
fptr – указатель на функцию с параметрами типа указателя на char и типа int , возвращающую указатель на int.
ptr - указатель на функцию с параметрами типа указателя на char, возвращающую значение типа int
Теперь указателю – переменной можно присвоить значения указателей – констант - имена конкретных функций, тип и спецификации которых должны полностью соответствовать указателю, и с помощью указателя вызывать эти функции :
( * имя указателя) (список фактических параметров)
int len ( char * e) // функция вычисления длины строки
{ int m=0;
while(e[m++] ) ;
return m-1 ;}
void main ()
{ ptr = len ;
char s [ ] =” rtgcerygw”;
int n;
n = (* ptr) ( s) // ptr (s) – так тоже можно вызывать
Указателю – переменной можно присваивать имена различных функций ( указателей – констант ), у которых соответствующий указателю тип результата и та же сигнатура.
Пример : Вызов функций по адресам через указатель
#include <iostream. h>
int add ( int n, int m ) { return ( n+m) ;}
int div ( int n, int m ) { return ( n / m) ;}
int mult ( int n, int m ) { return ( n * m) ;}
int subt ( int n, int m ) { return ( n - m) ;}
void main ( )
{ int ( * ptr) ( int, int ) ;
// объявлен указатель на функцию
int a, b ; char c;
cin >> a >> c >> b;
switch (c)
{ case ‘+’ : ptr = add ; break ;
case ‘-‘ : ptr = subt ; break;
case ‘ * ‘ : ptr = mult ; break;
case ‘ / ‘ : ptr = div ; break;
}
cout << a << c <<b<< “ =’’ << (*ptr) (a, b ) // ptr(a, b);
Массивы указателей на функции:
float ( * ptrA ) ( char , int ) [4] ;- массив из 4 –х указателей
Или можно объявить так:
float ( * ptrA [ 4 ] ) ( char, int ) ;
Можно при объявлении провести инициализацию массива указателей на функции именами соответствующих функций:
float v1 ( char s, int n) { …}
…
float v4 ( char s, int n) { …}
float (* ptrr [4]) = { v1, v2, v3, v4 };
float x = (*ptrr [3] ) ( ‘a’ , 5) – вызов 4-ой функции
Это сложно, особенно если эти описания используются для описания параметров других функций. Определить тип указателя на функции с помощью спецификатора typedef
Вводится описание типа указателя на функцию:
typedef <тип возвращаемого результата> ( * имя типа указаспецификация параметров);
typedef int (* ptr) ( int); // ptr - тип указателя на функции,
// возвращающие int и имеющие параметром также int
typedef void (* ptf ) ( ptr, int, char*);
Можно объявлять указатели и массивы указателей описанных типов
ptr A , B[4] ; // объявлен указатель и массив указателей на
// функции int имя ( int);
Массивы указателей на функции
Удобно использовать при разработке различных меню – вызова различных функций с помощью меню.
Надо выбрать один из видов обработки
# include < iostream. h>
# include < stdlib. h>
// определение функций для обработки меню :
void act1 ( ) { cout << “ чтение файла” }
void act2 ( ) { cout << “ модификация файла” }
void act3 ( ) { cout << “ дополнение файла” }
void act4( ) { cout << “ удаление записей файла” }
typedef void ( * menu) ()
menu act [4] = { act1, act2 , act3 , act 4};
void main ()
{ int n ; // номер пункта меню
cout << “\n1 - чтение файла”;
cout << “\n2 - модификация файла”;
cout << “\n3 - дополнение файла”;
cout << “\n4 - удаление записей файла”;
while (1) { cout << “\n введите номер”; cin >>n ;
if ( n >= 1 && i<= 4) act [n-1] ( ) ; else exit(0); } }
Указатель на функцию - параметр функции
для формирования
1) таблиц результатов, полученных с помощью различных формул;
2) для вычисления интегралов с различными подинтегральными функциями;
3) для нахождения сумм рядов с различными общими членами
и т. д.
Пример : Построение таблицы значений различных функций
# include <iostream. h>
typedef float (* func) ( float x) ;
float a ( float x) { return x*x }
float b ( float x) { return (x*x +100) }
float c ( float x) { return sqrt ( fabs(x)) +x;}
func S [3] = { a, b, c }
void table ( func ptrA [ 3] , float xn, float xk , float dx )
{ float x = xn;
while ( x<= xk )
{cout <<”\n”;
for (int i=0; i< 4; i++) { cout. width(10); cout <<(* ptrA[i] ) (x); }
x+=dx ;
}
}
void main { table ( S, 0. , 2 . , 0.1 ) }
Указатель на функцию – результат работы функции
Функция может возвращать указатель на функцию в качестве результата
c помощью return
. . .typedef void ( * menu) ( )
menu act [4] = { act1, act2 , act3 , act 4};
menu V (int i) { return act [i] } // функция возвращает указатель на
// функцию как результат работы функции
void W (int i , menu & f) { f = act [i] } // функция возвращает указатель на
// функцию, используя формальный параметр
//( передача параметра по ссылке)
void main ( )
{ int i ;
menu nf, ff;
while(1) { cin >> i ; if ( i>=1 && i<=4 )
{ nf = V ( i-1);
nf( ) ; // вызов функции
act [i-1] ( ) ; // вызов функции
W( i-1, ff) ;
ff ( ) ; // вызов функции
}
else exit ( 0) ;} }
Ссылка на функцию
Определение :
< тип функции> ( & имя ссылки ) ( спецификация параметров)
<инициализирующее выражение>;
<инициализирующее выражение> -имя уже известной функции, имеющей тот же тип и ту же сигнатуру параметров, что и ссылка.
Ссылка на функцию – синоним функции, обладает всеми правами основного имени функции
. . .
void func (char c) { cout <<c <<endl ;}
void main ( )
{ void (*pf) (char) = func;
void (& rf)(char) = func ;
(*pf) (‘A’) ; func ( ‘B’) ; rf (‘C’) ; }
A
B
C
Инициализация может быть и в круглых скобках
void (*pf) (char) (func) ;
void (& rf)(char)( func );
Ссылка - возвращаемый результат функции:
Пример : Функция определяет ссылку на элемент массива c максимальным значением
…
int & rmax ( int n, int d [ ] )
{ int imax =0;
for ( int i =1 ; i< n ; i++)
imax = d[imax] > d[i] ? imax :i ;
return (d [imax] );
void main ()
{ int n =5, a [ ] = { 3, 7 , 21 , 33 , 6};
cout << rmax (n, a );
rmax(n, a)=0;
For ( int i =0 ; i, n ; i++)
Cout << a[i] << “ “ ;}
Один из вызовов rmax находится в левой части оператора присваивания,
Что позволяет занести в элемент новое значение.
Рекурсивные функции
Рекурсия – это способ организации процесса вычислений при котором функция обращается сама к себе прямо или косвенно.
Функция называется косвенно рекурсивной, если она содержит обращение к другой функции, которая содержит прямой или косвенный вызов определяемой ( первой ) функции.
Если в теле функции явно используется вызов этой функции, то имеет место прямая рекурсия.
Рекурсивная форма алгоритма дает более компактный текст программы, но требует дополнительных затрат ОП для размещения данных и времени для рекурсивных вызовов функции.
Рекурсия не дает ни экономии памяти, ни быстродействия, дает экономия текста программы и облегчает ее понимание..
При выполнении рекурсивной функции происходит многократный ее вызов, при этом
1) в стеке сохраняются значения всех локальных переменных и
параметров функции для всех предыдущих вызовов, выделяется
память для локальных переменных очередного вызова;
2) внутренним переменным с классом памяти extern и static выделяется
один раз память, которая сохраняется в течении всего времени программы;
3) вызов рекурсивной функции происходит до тех пор пока не будет
получено конкретное значение без вызова функции ( для завершения
рекурсии).
Примеры :
//Функция , возвращающая факториал : N! = N * (N-1) !
long fact ( int k)
{ if ( k <0 ) return 0 ;
if ( k = = 0 ) return 1 ;
return k * fact ( k - 1) ; }
//Функция, возвращающая целую степень числа X n = X * Xn-1 , n >0
// X n = Xn+1 / X, n< 0
double step ( double X, int n )
{ if ( n = = 0 ) return 1;
if ( X = = 0) return 0 ;
if ( n > 0 ) return X * step ( X, n-1 );
if (n<0 ) return step ( X, n+1 ) / X;
}
// Функция возвращающая сумму массива: S nk = a[nk] + Snk-1
int sum (int a[] , int nk )
{ if (nk = = 0)
return ( a[0] );
else return ( a[nk] + sum ( a, nk-1 ) ) ; }
Рекурсию удобно использовать при вычислении сумм убывающих рядов.
Шаблоны функций
Шаблон семейства функций – это конструкция, позволяющая автоматически создавать функции, обрабатывающие разные типы данных.
Шаблон семейства функций определяется один раз, но это определение параметризуется. Параметрами в шаблоне могут быть типы любых параметров функций и тип возвращаемого функцией значения.
Для параметризации используется список формальных параметров шаблона, который следует после слова template, заключенный в угловые скобки < >. Каждый параметр обозначается словом class, за которым следует имя параметра. Имя параметра – это название типа, его можно использовать для обозначения типов параметров функции, типа возвращаемого результата и для объявления типов локальных переменных функции.
Примеры определения шаблонов функций:
template <class T>
void swap (T&x, T&y)
{ T z = x; x = y; y = z; }
template <class R>
R abs ( R x) { return x>0 ? x : - x;}
Если далее в программе обнаруживаются вызовы функций с данными именами, компилятор определяет соответствующие нужные функции.
…
abs(-45.8); //1
long a=4, b=5;
swap(a, b); //2
double c=3.8, d=6.8;
swap(c, d); //3
…
компилятор сформирует следующие определения функций:
double abs ( double x) { return x>0 ? x : - x;} //1
void swap (long &x, long &y) //2
{ long z = x; x = y; y = z; }
void swap (double &x, double &y) //3
{ double z = x; x = y; y = z; }
Еще один шаблон семейства функций, возвращающих наибольший элемент массива ( но не значение, а участок памяти, т. е. возвращается ссылка):
template <class type>
type & max ( int n, type A[ ] )
{ int imax =0;
for(int i=1 ; i< n ; i++)
if ( A[imax] < A[i] ) imax = i;
return A[imax];
}
void main()
{int x[4] = {…};
float y[3] = { …};
cout<< max ( 4, x) << max (3, y);
}
Свойства:
1) имя параметра уникально в шаблоне;
2) список параметров шаблона не может быть пустым;
3) каждый параметр обязательно определяется со словом class;
4) все параметры шаблона должны обязательно быть использованы в спецификации параметров функции
template <class A, class B, class C>
B func ( A r, B t ) { B v; …} -ошибка


