Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Указатели на функции.doc
Скачиваний:
24
Добавлен:
02.06.2015
Размер:
4.28 Mб
Скачать

Практическая работа №3 «Указатели на функции»

1. Указатели при вызове функций.

До сих пор мы рассматри­вали функцию как минимальный исполняемый модуль про­граммы, обмен данными с которым происходит через набор параметров функции и с помощью значений, возвращаемых функцией в точку вызова. Теперь перейдем к вопросу о том, по­чему в языке Си функция введена как один из производных ти­пов. Необходимость в таком типе связана, например, с задачами, в которых функция (или ее адрес) должна выступать в качестве параметра другой функции или в качестве значения, возвращаемого другой функцией. Вызов функции:

обозначение_функции (список_фактических_параметров)

где обозначение_функции (только в частном случае это иден­тификатор) должно иметь тип "указатель на функцию, возвра­щающую значение конкретного типа".

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

Самый употребительный указатель на функцию - это ее имя (идентификатор). Именно так указатель на функцию вводится в ее определении:

тип имя_функции (спецификация_параметров) тело_функции

Прототип

тип имя_функции (спецификация_параметров);

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

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

Указатель на функцию как переменная вводится отдельно от определения и прототипа какой-либо функции. Для этих целей используется конструкция:

тип (*имя_указателя) (спецификация_параметров);

где тип - определяет тип возвращаемого функцией значения;

имя_указателя - идентификатор, произвольно выбранный программистом;

спецификация_параметров - определяет состав и типы параметров функции.

Например, запись

int (*point) (void);

определяет указатель-переменную с именем point на функции без параметров, возвращающие значения типа int.

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

int * funct (void);

то это будет не определением указателя, а прототипом функции без параметров с именем funct, возвращающей значения типа int*.

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

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

point=funct; /* Ошибка - несоответствие типов */

будет ошибочной, так как типы возвращаемых значений для point и funct различны.

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

#include "stdafx.h"

#include "stdio.h"

#include "conio.h"

#include "locale.h"

void f1 (void)

{

printf("\n Выполняется f1( ) ");

}

void f2 (void)

{

printf ("\n Выполняется f2( )");

}

int _tmain(int argc, _TCHAR* argv[])

{

setlocale(LC_ALL,"Russian");

void (*point) (void); /*point - указатель-переменная на функцию */

f2 (); /* Явный вызов функции f2()*/

point=f2; /* Настройка указателя на f2()*/

(*point)(); /* Вызов f2() по ее адресу с разыменованием указателя */

point=f1; /* Настройка указателя на f1()*/

(*point)(); /* Вызов f1() по ее адресу с разыменованием указателя */

point (); /* Вызов f1() без разыменования указателя */

_getch();

return 0;

}

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

Выполняется f2( )

Выполняется f2( )

Выполняется f1( )

Выполняется f1( )

Все варианты обращения к функциям с использованием ука­зателей-констант (имен функций) и указателей-переменных (неконстантных указателей на функции) в программе продемон­стрированы и снабжены комментариями. Приведем общий вид двух операций вызова функций с помощью неконстантных ука­зателей:

(*имя_указателя) (список_фактических_параметров) имя_указателя (список_фактических_параметров)

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

*point (); /* Ошибочный вызов */

Это полностью соответствует синтаксису языка. Операция '()' - "круглые скобки" имеет более высокий приоритет, чем операция разыменования '*'. В этом ошибочном вызове вначале выполнится вызов point(), а уж к результату будет применена операция разыменования.

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

int fic (char); /* Прототип функции */

int (*pfic) (char)=fic; /* pfic - указатель на функцию */

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