Мощность языка СИ во многом определяется легкостью и гибкостью в определении и использовании функций в СИ-программах. В отличие от других языков программирования высокого уровня в языке СИ нет деления на процедуры, подпрограммы и функции, здесь вся программа строится только из функций.
Функция - это совокупность объявлений и операторов, обычно предназначенная для решения определенной задачи. Каждая функция должна иметь имя, которое используется для ее объявления, определения и вызова. В любой программе на СИ должна быть функция с именем main (главная функция), именно с этой функции, в каком бы месте программы она не находилась, начинается выполнение программы.
Указатель - это адрес памяти, распределяемой для размещения идентификатора (в качестве идентификатора может выступать имя переменной, массива, структуры, строкового литерала). В том случае, если переменная объявлена как указатель, то она содержит адрес памяти, по которому может находится скалярная величина любого типа. При объявлении переменной типа указатель, необходимо определить тип объекта данных, адрес которых будет содержать переменная, и имя указателя с предшествующей звездочкой (или группой звездочек).
Указатель на функцию содержит адрес функции в памяти. Имя массива на самом деле является адресом первого элемента массива. Аналогично, имя функции начальный адрес ее кода. Указатели на функции можно передавать функциям, возвращать из функций, хранить в массивах и присваивать другим указателям на функции.
Чтобы проиллюстрировать использование указателей на функции, модифицируем программу пузырьковой сортировки и сформируем программу многоцелевой сортировки, использующая указатели на функции.
Данная программа содержит main и функции bubble, swap, ascending и descending. Функция bubbleSorting принимает указатель на функцию - либо функцию ascending, либо функцию descending - как аргумент и, кроме того, аргументы вида массива целых чисел и размера этого массива. Программа прелагает выбрать, в каком порядке должен быть отсортирован массив - возрастающем или убывающем. Если ввести 1, то в функцию bubble передается указатель на функцию ascending, приводящую к сортировке массива в возрастающем порядке. Если пользователь вводит 2, то в функцию bubble передается указатель на функцию descending, приводящую к сортировке массива в возрастающем порядке. Выходные данные программы.
В заголовке функции bubble появляется следующий параметр:
int (*compare) (int, int)
Это сообщает bubble о том, что она должна ждать параметр, являющийся указателем на функцию, которая принимает два целых параметра и возвращает целый результат. Скобки вокруг *compare нужны потому, что * имеет приоритет ниже, чем скобки, в которые заключены параметры функции.
Если не включить скобки, объявление имело бы вид
int *compare (int, int)
что объявляло бы функцию, которая принимает два целых как параметры и возвращает указатель на целое.
Соответствующий параметр в прототипе функции bubble имеет вид
int (*) (int, int)
В данном прототипе включены только типы, но для целей докуменирования можно включить и имена, которые компилятор будет игнорировать.
Функция, переданная bubble, вызывается в операторе if следующим образом
if ((*compare)(work[count], work [count+1]))
которое использует указатель непосредственно как имя функции. Первый метод вызова функции посредством указателя предпочтительнее, потому что он явно показывает, что compare является указателем на функцию, который разыменовывается, чтобы вызвать функцию. Второй метод вызова функции посредством указателей представляет это так, будто compare является подлинной функцией. Это может смутить пользователя программы, который захотел бы увидеть определение функции compare и найти в файле то, что в нем не определено.
Типичным применением указателей на функцию являются так называемые системы, управляемые меню. В них предлагается выбрать позиции меню (например, от 1 до 5). Каждая позиция обслуживается своей определенной функцией. Указатели на каждую функцию хранятся в массиве указателей на функции. Выбор пользователя используется как индекс массива, а указатель в массиве используется для вызова функции.
Программа демонстрации массива указателей на функции представляет обобщающий пример механизма объявления использования массива указателей на функции. В примере определены три функции - function1, function2 и function3 - каждая из которых принимает целый аргумент и ничего не возвращает. Указатели на эти три функции хранятся в массиве f, который объявлен следующим образом
void (*f3[3])(int) = {function1, function2, function3};
Прочесть это объявление можно так: "f является массивом трех указателей на функции, которые берут аргументы int и возвращают void" Массив получает в качестве начальных значений имена трех функций. Когда пользователь вводит значения между 0 и 2, это значение используется в качестве индекса в массиве указателей на функции. Вызов функции выполняется так:
(*f[choice])(choice);
В этом вызове f [choice] выделяется указатель, расположенный в элементе массива с индексом choice. Указатель разыменовывается, чтобы вызвать функцию, и choice передается функции как аргумент. Каждая функция печатает значения своих аргументов и имя функции, чтобы показать, что функция вызывается правильно.