Партнерка на США и Канаду по недвижимости, выплаты в крипто

  • 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; …} -ошибка