typedef int arr 5[5]; typedef arr5 * р_arr5; typedef p_arr5 arrрЮ [10];
arr5 togs; // togs - массив из 5 значений int
р_arr5 р2; // р2 - указатель на массив из 5 значений int
arrрЮ ар; //ар - массив, содержащий 10 указателей на массивы из 5 значений int
Когда вы освоитесь с созданием структур подобного рода, возможности для самых причудливых объявлений действительно возрастут. Что же касается их применений, то мы оставляем эти вопросы за более сложными источниками.
Функции и указатели
Как продемонстрировало обсуждение объявлений, допускается объявлять указатели на функции. Возможно, вас интересует, в чем они могут быть полезны. Обычно указатель на функцию используется в качестве аргумента в другой функции, сообщая ей, какую функцию применять. Например, сортировка массива предполагает сравнение двух элементов для выяснения того, какой из них должен следовать первым. В случае числовых элементов можно использовать операцию >. Но в целом элементом может быть строка или структура, что требует вызова специальной функции для выполнения
Структуры и другие формы данных 613
сравнения. Функция qsort() из библиотеки С спроектирована на работу с массивами любого вида при условии, что вы уведомите ее о том, какую функцию применять для сравнения элементов. С этой целью qsort() принимает в одном из своих аргументов указатель на функцию. Затем qsort() использует указанную функцию для сортировки значений определенного типа — будь он целочисленным, строкой или структурой.
Давайте подробнее рассмотрим указатели на функции. Прежде всего, что они означают? Скажем, указатель на int содержит адрес ячейки памяти, в которой может быть сохранено значение int. Функции также имеют адреса, поскольку реализация функции на машинном языке состоит из кода, загружаемого в память. Указатель на функцию может содержать адрес, помечающий начало кода функции.
Далее, когда вы объявляете указатель на данные, то должны объявить тип данных, на которые он указывает. При объявлении указателя на функцию необходимо объявить тип указываемой функции. Чтобы задать тип функции, понадобится указать сигнатуру функции, т.е. возвращаемый тип и типы параметров функции. Например, взгляните на следующий прототип:
void ToUpper(char *); // преобразует строку в верхний регистр
Тин ToUpper() определен как “функция с параметром char * и возвращаемым типом void”. Вот как объявить указатель на функцию такого типа по имени pf:
void (*рf) (char *); // pf - указатель на функцию
Читая это объявление, вы видите, что первая пара круглых скобок связывает операцию * с pf, т.е. pf является указателем на функцию. Это делает (*рf) функцией, a (char * ) — списком ее параметров функции и void — возвращаемым типом. Вероятно, проще всего понять, как создано такое объявление — обратить внимание, что имя функции ToUpper в нем заменено выражением (*pf). Таким образом, если вы хотите объявить указатель на специфичный тип функции, можете объявить функцию этого типа и затем заменить имя функции выражением вида (*pf), получив в результате объявление указателя на функцию. Как упоминалось ранее, первые круглые скобки необходимы из-за правил, регламентирующих приоритеты операций. Если их отбросить, получится что-то совершенно другое:
void *pf(char *); // pf - функция, которая возвращает указатель
Совет
Чтобы объявить указатель на функцию конкретного типа, сначала объявите функцию желаемого типа и затем замените имя функции выражением в форме (*pf); после этого pf становится указателем на функцию данного типа.
Указателю на функцию можно присваивать адрес функции подходящего типа. В этом контексте для представления адреса функции может применяться ее имя:
void ToUpper(char *); void ToLower(char *); int round(double); void (*pf) (char *);
pf = ToUpper; // допустимо, ToUpper - адрес функции
pf = ToLower; // допустимо, ToLower - адрес функции
pf = round; // недопустимо, round - неподходящий тип функции
pf = ToLower(); // недопустимо, ToLower () не является адресом
Последнее присваивание также недопустимо, потому что нельзя использовать функцию void в операторе присваивания.
614 глава 14
Обратите внимание, что указатель pf может указывать на любую функцию, которая принимает аргумент char * и имеет возвращаемый тип void, но не на функции с другими характеристиками.
Подобно тому, как можно применять указатель на данные с целью доступа к ним, вы можете использовать указатель на функцию для обращения к этой функции. На удивление для этого существуют два логически несогласованных синтаксических правила, как иллюстрируется в следующем фрагменте:
Читать дальше