Операция typedef
Любому типу данных, как стандартному, так и определенному пользователем, можно задать новое имя с помощью операции
typedef < . тип > . < . новое_имя > . .
Введенный таким образом новый тип используется аналогично стандартным типам, например, введя пользовательские типы:
typedef int INT .
typedef char Mass_str[100] .
декларации идентификаторов введенных типов имеют вид:
INT i, j . → две переменные i и j типа int .
Mass_str str[10] . → массив str из 10 строк по 100 символов.
В языке Си идентификатор функции является константным указателем на начало функции в оперативной памяти и не может быть значением переменной. Но имеется возможность декларировать указатели на функции с которыми можно обращаться как с переменными (например, можно создать массив, элементами которого будут указатели на функции).
Рассмотрим методику работы с указателями на функции:
1. Как и любой объект языка Си, указатель на функции необходимо декларировать. Формат объявления указателя на функции следующий:
тип (* переменная-указатель ) (список параметров) .
т.е. декларируется указатель, который можно устанавливать на функции, возвращающие результат указанного типа и имеют указанный список параметров. Наличие первых круглых скобок обязательно, так как без них –это декларация функции, которая возвращает указатель на результат своей работы указанного типа и имеет указанный список параметров. Например, объявление вида:
float (*p_f) (char, float) .
говорит о том, что декларируется указатель p_f, который можно устанавливать на функции, возвращающие вещественный результат и имеющие два параметра: первый – символьного типа, а второй – вещественного типа.
2. Идентификатор функции является константным указателем, поэтому для того чтобы установить переменную-указатель на конкретную функцию, достаточно ей присвоить идентификатор этой функции:
переменная-указатель = имя_функции .
Например, имеется функция с прототипом
float f1 (char, float) .
тогда операция
p_f = f1 .
установит указатель p_f на данную функцию.
3. Вызов функции после установки на нее указателя выглядит так:
(*переменная-указатель)(список аргументов) .
или
переменная-указатель (список аргументов) .
После таких действий кроме стандартного обращения к функции
имя_функции(список аргументов) .
появляется еще два способа вызова функции:
(*переменная-указатель)(список аргументов) .
или
переменная-указатель (список аргументов) .
Последнее справедливо, так как p_f также является адресом начала функции в оперативной памяти. Для нашего примера к функции f1 можно обратиться следующими способами:
f1(‘z’, 1.5) . // Обращение к функции по имени
(* p_f)(‘z’, 1.5) . // Обращение к функции по указателю
p_f(‘z’, 1.5) . // Обращение к функции по имени указателя
4. Пусть имеется вторая функция с прототипом:
float f2 (char, float) .
тогда, переустановив указатель p_f на эту функцию:
p_f = f2 .
имеем опять три способа ее вызова:
f2(‘z’, 1.5) . // по имени функции
(* p_f)(‘z’, 1.5) . // по указателю на функцию
p_f(‘z’, 1.5) . // по имени указателя на функцию
Основное назначение указателей на функции – это обеспечение возможности передачи идентификаторов функций в качестве параметров в функцию, которая реализует некоторый вычислительный процесс, используя формальное имя вызываемой функции.
Примеры работы с функциями
Пример указатель на функцию:
Написать функцию вычисления суммы sum, обозначив слагаемое формальной функцией fun(x), а при вызове функции суммирования передавать через параметр реальное имя функции, в которой запрограммирован явный вид этого слагаемого. Например, пусть требуется вычислить две суммы:
Поместим слагаемые этих сумм в пользовательские функции f1 и f2. При этом для удобства работы воспользуемся операцией typedef, введем пользовательский тип данных: указатель на функции, который можно устанавливать на функции, возвращающие результат, указанного типа, и имеющие указанный список параметров:
typedef тип_результата (* переменная-указатель)(параметры) .
Тогда в списке параметров функции суммирования достаточно указывать фактические имена функций данного типа. Программа для решения данной задачи может быть следующей:
#include < .iostream.h> .
typedef float (*p_f)(float) . // Декларация указателя на функциию
float sum(p_f fun, int, float) . // Декларации прототипа функции
float f1(float) . // Декларации прототипа функции
float f2(float) . // Декларации прототипа функции
void main(void)
{
float x, s1, s2 .
int n .
cout< .< . Введите кол-во слагаемых n:
cin> .> .n .
cout< .< . Введите значение x: .
cin> .> .x .
s1=sum(f1, 2*n, x) .
s2=sum(f2, n, x) .
cout< .< .nN = < .< . n .
cout < .< .nX = < .< . x .
cout< .< .nСумма 1 = < .< . s1< .< . .
cout< .< . nСумма 2 = < .< . s2< .< . .
}
// Функция вычисления суммы, первый параметр которой – формальное имя функции, //имеющей тип, введенный с помощью операции typedef
float sum(p_f fun, int n, float x)
{
float s=0 .
for(int i=1 . i< .=n . i++) s+=fun(x) .
return s .
}
float f1(float r) // Первое слагаемое
{
return (r/5.) .
}
float f2(float r) // Второе слагаемое
{
return (r/2.) .
}
Пример передача в функцию двухмерного массива:
Ввести массив NxN (не больше 50) целых чисел, в функции подсчитать сумму его положительных элементов.
#include < .iostream.h> .
void vvod(int, int, int a2[ ][50]) . // прототип функции ввод массива
int summa(int, int, int a1[ ][50]) . // прототип функции поиск суммы
void main(void)
{
int a[50][50] .
int i,j,Nstr,Nstb .
cout< .< .n Введите количество строк матрицы (< .50) .
cin> .> .Nstr .
cout< .< .n Введите количество столбцов матрицы (< .50) .
cin> .> .Nstb .
vvod(Nstr,Nstb,a) . // вызов функции ввод массива
cout< .< .an Сумма = < .< . summa(Nstr,Nstb,a) . // вызов функции поиск суммы и вывод на экран
cout< .< . nPress any key… .
}
void vvod(int nstr, int nstb, int a2[][50]) // функция ввод массива
{
int i,j .
cout< .< .n Введите массив n .
for(i=0 . i< .nstr . i++)
for(j=0 . j< .nstb . j++)
{
cout< .< .a[< .< .i+1< .< .][< .< .j+1< .< .]= .
cin> .> .*(*(a2+i)+j) .
}
}
int summa(int nstr, int nstb, int a1[ ][50]) //функция поиск суммы
{
int i,j,s .
cout< .< .n Функция summa .
for (s=0,i=0 . i< .nstr . i++)
for (j=0 .j< .nstb .j++)
if (a1[i][j]> .0) s+=a1[i][j] .
return s .
}
Пример передача в функцию двухмерного массива (с динамическим выделением памяти):
Ввести массив NxN (не больше 50) целых чисел, в функции подсчитать сумму его положительных элементов.
#include < .iostream.h> .
#include < .conio.h> .
void vvod(const int, const int, int **a2) . // прототип функции ввод массива
void summa(const int, const int, int **a1) . // прототип функции поиск суммы
void main(void)
{
int i,j,Nstr, Nstb .
cout< .< .n Введите количество строк матрицы (< .50)n .
cin> .> .Nstr .
cout< .< . Введите количество столбцов матрицы (< .50)n .
cin> .> .Nstb .
int **a .
// выделение памяти под матрицу а
a=new int* [Nstr] .
for(i=0 . i< .Nstr . i++)
a[i]=new int[Nstb] .
vvod(Nstr,Nstb,a) . // вызов функции ввод массива
summa(Nstr,Nstb,a) . // вызов функции поиск суммы
}
void vvod(const int nstr, const int nstb, int **a2) // функция ввод массива
{
int i,j .
cout< .< .n Введите массив n .
for(i=0 . i< .nstr . i++)
for(j=0 . j< .nstb . j++)
{
cout< .< .a[< .< .i+1< .< .][< .< .j+1< .< .]= .
cin> .> .*(*(a2+i)+j) .
}
}
void summa(const int nstr, const int nstb, int **a1) //функция поиск суммы
{
int i,j,s .
cout< .< .n Функция summa .
for (s=0,i=0 . i< .nstr . i++)
for (j=0 .j< .nstb .j++)
if (*(*(a1+i)+j)> .0) s+=*(*(a1+i)+j) .
cout< .< .n Сумма = < .< . s < .< . n Press any key… .
}