Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЗФ_ОАиП / Лекции ГГУ Скорины - Программирование.doc
Скачиваний:
179
Добавлен:
21.03.2016
Размер:
2.27 Mб
Скачать

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

С функцией в языке С можно проделать только две вещи: вызвать ее и получить ее адрес. Указатели на функции – очень мощное средство языка С.

Функции для процессора – те же двоичные коды, и с виду они мало отличаются от строк или массивов. Так же, как и массивы, они занимают последовательные ячейки памяти, где хранятся двоичные числа. Разница в том, что эту последовательность ячеек процессор воспринимает не как идущие подряд числа или буквы, а как последовательность команд.

Функция располагается в памяти по определенному адресу, который можно присвоить указателю в качестве его значения. Адресом функции является ее точка входа. Именно этот адрес используется при вызове функции. Так как указатель хранит адрес функции, то она может быть вызвана с помощью этого указателя. Он позволяет также передавать ее другим функциям в качестве аргумента.

Указатель на функцию имеет тип «указатель на функцию, которая возвращает значение заданного типа и имеет аргументы заданного типа»:

тип (*имя)(список типов аргументов);

Например, объявление:

int (*pfunc)(int, int);

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

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

int sum(int a,int b){

return a+b;

}

void main(){

int (*f)(int, int); // описание указателя на функцию

int (*ff)(int, int); // описание указателя на функцию

int k;

k = sum(5,9); // обычный вызов функции sum()

f = ∑ // можно так занести в указатель адрес функции sum(),

ff = sum; // а можно и так занести в указатель адрес функции sum()

k = (*f)(4,1); // можно так вызвать функцию через указатель,

k = ff(11,22); // а можно и так вызвать функцию через указатель

}

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

Вывод: Имя функции и указатель на нее практически равноправны. Разница между ними в том, что имя функции нельзя отделить от нее самой, оно всегда связано с постоянным адресом. Указатель же более свободен, и его можно направить куда угодно.

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

Например, в программе обработки меню часто вызываются различные функции для разных пунктов меню. В таких случаях чаще всего используют оператор switch с длинным списком меток case. Другой подход – создать массив функций и вызывать их с помощью индексов – делает программу менее громоздкой и, соответственно, менее подверженной ошибкам.

Задача. Организовать обработку пунктов меню с помощью оператора switch.

#include <stdio.h>

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

#include <bios.h>

void f0() { // обработка пункта меню 0 – выход из программы

printf("menu 0\n");

exit(0); // завершить работу программы

}

void f1() { // обработка пункта меню 1

printf("menu 1\n");

}

void f2() { // обработка пункта меню 2

printf("menu 2\n");

}

void f3() { // обработка пункта меню 3

printf("menu 3\n");

}

void main() {

int i;

while(1) { // цикл по обработке пунктов меню

printf("Введите номер пункта меню : ");

scanf("%d",&i);

switch(i) {

case 0: f0(); break;

case 1: f1(); break;

case 2: f2(); break;

case 3: f3();

}

bioskey(0);

}

}

Задача. Организовать обработку пунктов меню с помощью массива указателей на функции.

void f0() {

printf("menu 0\n");

exit(0);

}

void f1() {

printf("menu 1\n");

}

void f2() {

printf("menu 2\n");

}

void f3() {

printf("menu 3\n");

}

void main() {

int i;

// описание массива указателей на функции и его инициализация

void (*ff[4])(void) = {f0, f1, f2, f3};

// описание массива указателей на функции и занесение в него адресов функций

// void (*ff[4])(void);

// ff[0] = f0;

// ff[1] = f1;

// ff[2] = f2;

// ff[3] = f3;

while(1) {

printf("Введите номер пункта меню : ");

scanf("%d",&i);

ff[i](); // вызов функции, адрес которой занесен в i-ый элемент массива

bioskey(0);

}

}

Задача. Пример передачи адреса функции как параметра в функцию.

void f1() {

printf("Функция 1\n");

}

void f2() {

printf("Функция 2\n");

}

void fp(void (*ptrF)(void)) {

ptrF(); // вызов функции, адрес которой передан как параметр

}

void main() {

void (*f)(void);

fp(f1);

f = f2;

fp(f);

}