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

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

Увы, да! Человеческая память избавляется от всего того, что не требуется какое-то длительное время. Эдакий гарбидж-коллектор. Не-е, наверно не GC, а скорее менеджер памяти, который очищает кэш, а долго неиспользуемые данные вытесняет на диск. Ну, не важно!

Важно то, что листая старую книжку по С++, я внезапно для себя обнаружил, что я помню не все тонкости.

В общем, следующее повествование — это моя личная шпаргалка по указателям, написанная чисто для личного использования.

Допустим, имеется несколько однотипных функций:

void func1(int num, char *str);
void func2(int qty, char *msg);
void func3(int size, char *buf);

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

Поскольку функции однотипные, то у них общий тип. Сначала я напишу так:

void (*pf)(int, char*);

, здесь pf — имя переменной, которая указывает на этот тип. Конструкция *pf — это значение переменной, на которую указывает указатель pf. Сам же тип описывается ещё проще:

void (*)(int, char*);

Но если мы напишем так:

void *pf(int, char*);

, то это будет не определение переменной (тип которой указывает на функцию), а определение самой функции. Функции, которая возвращает указатель на void.

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

typedef void (*PFI)(int, char*);

, здесь PFI — это имя нового (пользовательского) типа. Тип — указатель на функцию, которая принимает два параметра и ничего не возвращает.

Но вообще новые типы лучше оформлять согласно негласному правилу — с использованием суффикса _t. Например так:

typedef void (*pf_t)(int, char*);

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

PFI pf1;   // Первый способ задания имени типа
pf_t pf2;  // Второй способ задания имени типа

Здесь pf1 и pf2 — это только имена переменных, которым нужно будет присвоить какие-то конкретные значения. Давайте сделаем это — присвоим им адреса конкретных функций:

pf1 = func1;
pf2 = func2;

Имя функции (func1, func2) — это и есть её адрес. Нам не надо делать так:

pf1 = &func1;
pf2 = &func2;

Довольно-таки изящное решений, не правда ли! Теперь мы можем вызывать ту или иную функцию (в зависимости от того, что мы присвоили переменной):

pf1(123, "Hello!");
pf2(456, "Привет!");

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

Более того, мы можем даже определить массив (из 10-ти) указателей на функции:

void (*pfa[10])(int, char*);

Здесь pfa — имя массива.

А если мы уже где-то ранее определили тип, то объявление массива будет выглядеть менее страшно:

pf_t pfa[10];

Здесь pf_t — это имя типа, а pfa — имя массива.

Инициализация элементов массива выглядит очень просто и очень понятно:

pfa[0] = func1;
pfa[1] = func2;
pfa[2] = func3;
...

Вызов функции из массива осуществляется по индексу:

int i;

i = 3;

pfa[i](789, "Hi there!");

Ну вот, и освежили память!
И всё-таки, надо признаться, у меня получилась глава из учебника, не краткая подсказка себе. Блин!

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s