Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lek9-10.doc
Скачиваний:
5
Добавлен:
15.11.2018
Размер:
217.6 Кб
Скачать

Int *pmin, I; // Рабочий указатель, содержащий результат

for (i = 1, pmin=A; i<n; i ++)

if (A[i] < *pmin) pmin = &A[i];

return(pmin); } // В операторе return - значение указателя

Void main() {

int B[5]={3,6,1,7,2}; printf("min = %d\n",*min(B,5)); }

Прежде всего обратим внимание на синтаксис. Заголовок функции написан таким образом, как будто имя функции является указателем на int. Этим способом и обозначается, что ее результат - указатель. Оператор return возвращает значение переменной-указателя pmin, то есть адрес. Вообще в нем может стоять любое выражение, значение которого является указателем, например:

return &A[k]; return pmin + i; return A+k;

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

В Си возможна также передача ссылки в качестве результата функции. Ее следует по­нимать как отображение (синоним) на пере­менную, которая возвращается оператором return. Требования к объекту - источнику ссылки, на который она отображается, еще более строгие - это либо глобальная перемен­ная, либо формальный параметр функции, пе­редаваемый в нее по ссылке или по указателю. При обращении к результату функции - ссылке производится действие с перемен­ной-прототипом.

// Функция возвращает ссылку на минимальный элемент массива

int &ref_min(int A[], int n){

for (int j=0,k=0; i<n; i++) if (A[i]<A[k]) k = i;

return A[k];}

void main(){

int В[5]={4,8,2|6,4};

ref_min(B,5)++;

for (int i=0; i<5; i++) printf("%d *',B[i]);

Указатели на функции

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

int foo(long x);

int bar(long x);

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

int (*functptr)(long x);

functptr = &foo;

(*functptr)(2);

functptr = &bar;

(*functptr)(4);

Начнем с краткого обзора простых указателей на функции. В С, и С++ в частности, указатель на функцию с именем my_func_ptr , указывающий на функцию, принимающую в качестве аргументов int и char*, и возвращающую float, объявляется так:

float (*my_func_ptr)(int, char*);

// Для большей удобочитаемости, рекомендую использовать typedef.

// Особенно можно запутаться, когда указатель на функцию является аргументом

// функции.

// Тогда бы объявление выглядело так:

typedef float (*MyFuncPtrType)(int, char*);

MyFuncPtrType my_func_ptr;

Заметьте, что различным комбинациям аргументов соответствуют различные типы указателей на функцию. В MSVC, кроме этого, указатели на функцию различаются в зависимости от типа соглашения о вызове (calling conventions): __cdecl, __stdcall, __fastcall. Для того чтобы указатель на функции указывал на вашу функцию, необходимо выполнить следующую конструкцию:

my_func_ptr = some_func;

Для вызова функции через указатель:

(*my_func_ptr)(7, "Arbitrary string");

Разрешается приводить один тип указателя на функцию к другому. Но не разрешается приводить указатель на функцию к указателю на void. Остальные разрешенные операции тривиальны. Указателю на функцию можно присвоить 0 для обозначения нулевого указателя. Доступны многочисленные операторы сравнения (==, !=, <, >, <=, >=), можно также проверить на равенство 0 либо неявным преобразованием к bool. Кроме того, указатель на функцию может быть использован в качестве нетипизированного параметра шаблона. Он в корне отличается от типизированного параметра, а также отличается от интегрального нетипизированного параметра шаблона. При инстанцировании используется имя, а не тип или значение. Именные параметры шаблонов поддерживаются не всеми компиляторами, даже не всеми из тех, которые поддерживают частичную специализацию шаблонов.

Наиболее распространенное применение указателей на функции в С – это использование библиотечных функций, таких как qsort, и обратных (callback) функций в Windows. Кроме того, есть еще много вариантов их применения. Реализация указателей на функции проста: это всего лишь «указатели на код», в них содержится начальный адрес участка ассемблерного кода. Различные типы указателей существуют лишь для уверенности в корректности применяемого соглашения о вызове.

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

Пример:

#include

#include

double cos(double);

double sin(double);

double tan(double);

int main()

{ double (*(*masfun))(double);

double x=0.5, y;

int i;

masfun=(double(*(*))(double))

calloc(3,sizeof(double(*(*))(double)));

masfun[0]=cos;

masfun[1]=sin;

masfun[2]=tan;

for (i=0; i<3; i++); { y="masfun[i](x);" printf("\n x="%g" y="%g",x,y);" } return 0; }

Указатели на функции как параметры

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

Рассмотрим задачу вычисления корня функции f(x) на интервале локализации [А; В] для иллюстрации особенностей применения указателя функции в качестве параметра. Численный метод (деление пополам интервала локализации) оформляется в виде функции со следующим заголовком:

float root(указатель_на_функцию, float A, float В, float EPS);

Здесь А - нижняя граница интервала локализации корня; B - верхняя граница того же интервала; EPS - требуемая точность определения корня. Введем тип "указатель на функцию", для которой нужно определить корень:

typedef float(*pointPunc)(float);

Теперь можно определить функцию для вычисления корня заданной функции с указателем pointPunc. Прототип функции будет таким:

float root(pointPunc F, float A, float B, float EPS);

Приведем тестовую программу для определения с помощью root() корня функции у = х2 - 1 на интервале [0, 2]:

//RAZN3_1.СРР - указатель функции как параметр функции.

#include <iostream.h>

#include <stdlib.h> // Для функции exit().

// Определение типа указателя на функцию:

typedef float (*pointFunc)(float);

// Определение функции для вычисления корня:

float root(pointFunc F, float A, float B, float EPS)

{

float x, y, c, Fx, Fy, Fc;

x = A; y = B;

Fx = (*F)(x); // Значение функции на левой границе.

Fy = (*F)(y); // Значение функции на правой границе.

if (Fx * Fy > 0.0)

{

cout << "\nНеверен интервал локализации";

exit(1); // Аварийное завершение программы.

}

do

{

c = (y - x)/2; // Центр интервала локализации.

Fc = (*F)(c); // Значение функции в с.

if (Fc * Fx > 0) { x = c; Fx = Fc; }

else { y = c; Fy = Fc; }

} while (Fc != 0 && y - x > EPS);

return c;

}

#include <math.h>

// Определение тестовой функции у = х * х - 1:

float testfunc(float x)

{ return x * x - 1; }

void main()

{

float root(pointFunc, float, float, float); // Прототип.

float result;

result = root(testfunc, 0.0, 2.0, 1e-5);

cout << "\nКорень тестовой функции: " << result;

}

Текст этой программы можно взять здесь.

Результат выполнения программы:

Корень тестовой функции: 1

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

int (*ff(int))(int*, int);

ff() объявляется как функция, имеющая один параметр типа int и возвращающая указатель на функцию типа

int (*)(int*, int);

И здесь использование директивы typedef делает объявление понятнее. Объявив PF с помощью typedef, мы видим, что ff() возвращает указатель на функцию:

// Использование директивы typedef делает

// объявления более понятными

typedef int (*PF)(int*, int);

PF ff(int);

Типом возвращаемого значения функции не может быть тип функции. В этом случае выдается ошибка компиляции. Например, нельзя объявить ff() таким образом:

// typedef представляет собой тип функции

typedef int func(int*, int);

func ff(int); // ошибка: тип возврата ff() - функция

&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]