Лекция 5. Тип указатель: указатели на функции.
Краткая аннотация лекции.
В лекции рассматриваются определение указателя на функцию, способы объявления, адресуемость и обращение к функции через указатель, передачу фактических параметров указателю на функцию, использование указателя на функцию в качестве параметра функции.
Цель лекции: изучить указатели на функции и методы передачи функций как параметров, научиться использовать указатели на функции в программных кодах на языке C++.
Текст лекции.
Функции, как и элементы данных, имеют адреса. Адресом функции является адрес памяти, с которого начинается машинный код функции.
Для того, чтобы использовать в программе указатель на функцию, необходимо выполнить следующие действия:
· принять адрес функции;
· объявить указатель на функцию;
· использовать указатель на функцию для вызова этой функции.
Каждая функция характеризуется типом возвращаемого значения, именем и списком типов ее параметров. Если имя функции использовать без последующих скобок и параметров, то оно будет выступать в качестве указателя на эту функцию, и его значение будет определяться как адрес размещения функции в памяти (первый байт). Это значение можно будет присвоить другому указателю. Тогда этот новый указатель можно будет использовать для вызова функции. Указатель на функцию как переменная вводится отдельно от определения и объявления (прототипа) какой-либо функции.
Указатель на функцию – переменная, которая содержит адрес некоторой функции. Соответственно, косвенное обращение по этому указателю представляет собой вызов функции.
Синтаксис определения указателя на функцию:
тип_функции(*имя_указателя)(спецификация_параметров)
где тип_функции – определяет тип возвращаемого функцией значения;
имя_указателя – идентификатор;
спецификация_параметров – определяет состав и типы параметров функции.
Например:
int (*pf)(); // без контроля параметров вызова
int (*pf)(void); // без параметров, с контролем по прототипу
int (*pf)(int, char*); // с контролем по прототипу
В соответствии с принципом контекстного определения типа данных эту конструкцию следует понимать так: pf – переменная, при косвенном обращении к которой вызывается функция с соответствующим прототипом, например int_F(int, char*), то есть pf содержит адрес функции или указатель на функцию. Следует обратить внимание на то, что в определении указателя присутствует прототип – указатель ссылается не на произвольную функцию, а только на одну из функций с заданной схемой формальных параметров и результата.
В определении указателя количество и тип параметров должны совпадать с соответствующими типами в определении функции, на которую ставится указатель.
Например,
int (*func1Prt)(char);
задает определение указателя func1Prt на функцию с параметром типа char, возвращающую значение типа int.
Важнейшим элементом в определении указателя на функцию являются круглые скобки. Так следующий фрагмент:
int *func(char);
это не определение указателя, а объявление (прототип) функции c именем func и параметром типа char, возвращающей значение указателя типа int *. В этом случае указатель указывает на значение функции.
Если же выполнить объявление:
char *(*func2Prt)(char *,int);
то определение указателя func2Prt на функцию с параметрами типа указатель на char и типа int, возвращающую значение типа указатель на char.
Синтаксис вызова функции с помощью указателя:
(*имя_указателя)(список_фактических_параметров);
значением имя_указателя служит адрес функции, а с помощью операции разыменования * обеспечивается обращение по адресу к этой функции.
Арифметические операции над указателями на функции запрещены.
Указатели на функции в основном используются в следующих случаях.
· Многие библиотечные функции в качестве аргумента получают указатель на функцию. Например, функция сортировки qsort() получает четвертым аргументом указатель на составляемую пользователем функцию сравнения сортируемых элементов.
· Использование указателей на функции в качестве аргументов позволяет разрабатывать универсальные функции, реализующие известные алгоритмы или методы. Например, функции численного решения уравнений, интегрирования и дифференцирования.
· Указатели на функции могут использоваться для косвенного вызова резидентных программ, точка входа в которые записана в известное место памяти, например, по одному из неиспользуемых векторов прерываний.
В определении указателя на функцию тип возвращаемого значения, а также типы, количество, последовательность параметров должны совпадать с соответствующими типами и характеристиками параметров тех функций, адреса которых предполагается присваивать вводимому указателю при инициализации или с помощью оператора присваивания.
Пример 1.
//Определение и использование указателей на функции
#include "stdafx.h"
#include <iostream>
using namespace std;
//Определение и использование указателей на функции
void f1(); //объявление (прототип)функции f1
void f2(); //объявление (прототип)функции f2
int _tmain(int argc, _TCHAR* argv[]) {
void (*ptr)(); //ptr - указатель на функцию
f2(); //явный вызов функции f2
ptr=f2;//указателю присваивается адрес функции f2
(*ptr)();
//вызов функции f2 по ее адресу с разыменованием указателя
ptr=f1;//указателю присваивается адрес функции f1
(*ptr)();
//вызов функции f1 по ее адресу с разыменованием указателя
ptr(); // вызов функции f1 без разыменованием указателя
system("pause");
return 0;
}
//описание функции f1 и f2
void f1() {
cout << "Выполняется f1\n";
}
void f2() {
cout << "Выполняется f2\n";
}
Пример 2.
//Вариант 1 использования указателя на функцию
#include "stdafx.h"
#include <iostream>
using namespace std;
float plus(float, float); //Объявление (прототип) функции
int _tmain(int argc, _TCHAR* argv[]){
float x=2.1, y=4.89;
float (*func)(float,float);
//определение указателя func на функцию
printf("Сумма равна %.3f\n",plus(x,y));
func=plus;
//указателю присвоить адрес func точки входа в функцию plus
printf("(Используем указатель на функцию)
Сумма = %.3f\n",func(x,y));
system("pause");
return 0;
}
//Описание функции сложения двух аргументов
float plus(float a, float b) {
return a+b;
}
//Вариант 2 использования указателя на функцию
#include "stdafx.h"
#include <iostream>
using namespace std;
float plus(float, float); //Объявление (прототип)функции
int _tmain(int argc, _TCHAR* argv[]){
float x=2.1, y=4.89;
float (*func)(float, float)=+
//определение указателя на функцию plus
printf("Сумма равна %.3f\n",plus(x,y));
func=plus;
//указателю присвоить адрес точки входа в функцию plus
printf("(Используем указатель на функцию)
Сумма = %.3f\n",func(x,y));
system("pause");
return 0;
}
//Описание функции сложения двух аргументов
float plus(float a, float b) {
return a+b;
}
Указатели на функции как параметры позволяют создавать функции, реализующие тот или иной метод обработки другой функции, которая заранее не определена. Например, можно определить функцию для вычисления определенного интеграла от произвольной функции. Подынтегральная функция может быть передана в функцию вычисления интеграла с помощью параметра-указателя.
Пример 3: Вычислите приближенное значение интегралов с помощью формулы прямоугольников, задав пределы интегрирования [a, b] и число интервалов разбиения (N): и .
#include "stdafx.h"
#include <iostream>
using namespace std;
//Объявление (прототипы) функций:
/*функция rectangle() возвращает значение типа double, ее параметры:*/
/*pf–указатель на функцию с параметром типа double, возвращающую значение double*/
/*a, b – пределы интегрирования, величины типа double*/
double rectangle(double(*pf)(double), double a, double b);
/*функция ratio() возвращает значение типа double, ее параметр типа double*/
double ratio(double x);
/*функция cos4_2() возвращает значение типа double, ее параметр типа double*/
double cos4_2(double v);
int _tmain(int argc, _TCHAR* argv[]){
double a,b,c;
printf("\nВведите значения пределов интегрирования:");
printf("\na= ");
scanf("%lf",&a);
printf("\nb= ");
scanf("%lf",&b);
c=rectangle(ratio,a,b);
printf("Первый интеграл = %f\n",c);
printf("Второй интеграл = %f\n",rectangle(cos4_2,a,b));
system("pause");
return 0;
}
double rectangle(double(*pf)(double), double a, double b){
/*Вычисление определенного интеграла с помощью формулы прямоугольников*/
int N, i;
double h,s=0.0;
printf("\nВведите количество интервалов разбиения: N= ");
scanf("%d",&N);
printf("\na= ");
h=(b-a)/N; //Длина интервала разбиения
for (i=0;i<N;i++)
s+=pf(a+h/2+i*h);
return h*s;
}
double ratio(double x) { //Подынтегральная функция
double z; //Вспомогательная переменная
z=x*x+1;
return x/(z*z);
}
double cos4_2(double v){ //Подынтегральная функция
double w; //Вспомогательная переменная
w=cos(v);
return 4*w*w;
}
Ключевые термины
Адрес функции –это адрес памяти, с которого начинается машинный код функции.
Косвенное обращение по указателю на функцию – это вызов функции, адресуемой указателем.
Указатели на функции как параметры функции –это параметры функции, типы которых определены как указатели на функции.
Указатель на функцию– это переменная, которая содержит адрес этой функции.
Краткие итоги
1.Адресация функций осуществляется по первому байту расположения машинного кода функции в памяти.
2.Инициализированный указатель на функцию содержит адрес этой функции.
3.Арифметические операции над указателями на функции запрещены в силу их внутреннего представления.
4.При описании указателя на функцию необходимо соблюдать приоритет операций. Разыменование имеет низкий приоритет, поэтому порядок действий определяется круглыми скобками.
5.При описании указателя на функцию в качестве параметров можно указывать пустой список или список типов параметров.
6.При вызове функции через указатель необходимо, чтобы типы и набор фактических параметров совпадали с прототипом адресуемой функции.
7.Функции можно передавать в качестве параметров функций.
8.Указатели на функции и указатели как параметры функции имеют широкое практическое применение в программировании.
Набор для практики
Вопросы
1. Почему запрещены арифметические операции над указателями на функции?
2. Почему в описании указателя на функцию необходимы круглые скобки при имени указателя?
3. Может ли функция возвращать значение типа указатель? Если да, то как объявляется прототип такой функции?
4. Могут ли параметрами функции быть указатели на объекты? Если да, то как происходит передача фактических параметров при вызове функции?
5. Могут ли параметрами функции быть указатели на функции? Если да, то как происходит передача фактических параметров при вызове функции?
6. Как понимается следующее объявление:
float *(*func)(int(*pf)(char),float);?
7. В чем отличие результатов вызова функции через указатель с последующим разыменованием указателя и без разыменования указателя?
Упражнения
1.Наберите коды программ из Примеров 1-3. Выполните компиляцию и запуск программ.
2.Методом половинного деления решите уравнения на отрезке [a, b] с данной точностью е: , . При решении считать, что на данном отрезке существует единственный корень. Решите первое уравнение на отрезке [0, 1], а второе на отрезке [100, 150].
3.Решите задачу, используя один указатель на функцию. Разработайте четыре функции над двумя целыми параметрами, соответствующие арифметическим операциям (+, -, *, /). В основной программе задавайте два целых параметра и символьный знак операции до тех пор, пока не будет введен пробел в качестве знака операции. В выходных данных выводите значения функций.
4.Вычислите суммы с данной точностью е. Используйте указатели на функции как параметры. и .
Литература
1. Керниган, Б. Язык программирования Си / Б. Керниган, Д. Ритчи. – М.: Вильямс, 2007. – 304 с.
2. Подбельский, В.В. Практикум по программированию на языке Си: учеб. пособие / В.В. Подбельский. – М.: Финансы и статистика, 2004. – 576 с.
3. Подбельский, В.В. Программирование на языке Си: учеб. пособие / В.В. Подбельский, С.С. Фомин. – М.: Финансы и статистика, 2004. – 600 с.
4. Подбельский, В.В. Язык Си++: учеб. пособие / В.В. Подбельский. – М.: Финансы и статистика, 2005. – 560 с.
5. Романов, Е.Л. Практикум по программированию на языке С++: учеб. пособие / Е.Л. Романов. – СПб: БХВ-Петербург, 2004. – 432 с.
6. С/С++. Структурное программирование: практикум / Т.А. Павловская, Ю.А. Щупак. – СПб: Питер, 2004. – 239 с.
Дата добавления: 2017-06-02; просмотров: 1237;