Страница 80 из 82
Тип функции Основными характеристиками функции является тип возвращаемого значения и список типов формальных параметров. Подобно тому, как имена переменных никаким образом не влияют на их тип, имена функций не является частью их типа. Тип функции определяется типом возвращаемого значения и списком типов её формальных параметров. Например, пара функций char MyF1 (int, int, int*, float); char MyNew (int MyP1, int MyP2, int* MyP3, float MyP3); имеют один и тот же тип: char (int, int, int*, float) Подобную конструкцию мы назовём описанием типа функции. А вот как выглядит описание типа функции, которая возвращает указатель на объект типа char: char * (int, int, int*, float) Описанию этого типа соответствует, например, функция char *MyFp (int MyP1, int MyP2, int* MyP3, float MyP3); Комбинируя знак ptr-операции * с именем функции мы получаем новую языковую конструкцию: char (*MyPt1) (int MyP1, int MyP2, int* MyP3, float MyP3); Это уже не объявление функции. Это определение указателя на функцию! Это объект со следующими характеристиками: - его имя MyPt1,
- это указатель на функцию,
- эта функция должна возвращать значения типа char,
- список её формальных параметров имеет вид (int,int,int*, float).
Так что это должны быть функции со строго определёнными характеристиками. В нашем случае - это функции типа char (int, int, int*, float) Описание типа указателя на функцию, возвращающую указатель на объект типа char с параметрами (int, int, int*, float) char * (int, int, int*, float) отличается от описания типа этой функции дополнительным элементом (*): char * (*) (int, int, int*, float). Пример определения подобного указателя: char* (*MyPt2) (int MyP1, int MyP2, int* MyP3, float MyP3); И опять новый объект: - его имя MyPt2,
- это указатель на функцию,
- эта функция должна возвращать указатель на объекты типа char,
- список её формальных параметров имеет вид (int,int,int*, float).
Также можно определить функцию, которая будет возвращать указатель на объект типа void (то есть просто указатель). Это совсем просто: void * (int) Описанию этого типа соответствует, например, функция void *malloc (int size); Эта функция пытается выделить блок памяти размера size и в случае, если это удалось сделать, возвращает указатель на выделенную область памяти. В противном случае возвращается специальное значение NULL. Как распорядиться выделенной памятью - личное дело программиста. Единственное ограничение заключается в том, что при этом необходимо использовать явное преобразование типа: #include <stdlib.h> char *p = NULL; void NewMemory () { p = malloc(sizeof(char)*1024);// Этот оператор не пройдёт! p = (char*) malloc(sizeof(char)*1024); // Требуется явное преобразование типа. } Имя массива, если к нему не применяется операция индексации, оказывается указателем на первый элемент массива. Аналогично, имя функции, если к нему не применяется операция вызова, является указателем на функцию. В нашем случае ранее объявленная функция под именем MyFp приводится к безымянному указателю типа char * (*) (int, int, int*, float) К имени функции может быть применена операция взятия адреса. Её применение также порождает указатель на эту функцию. Таким образом, MyFp и &MyFp имеют один и тот же тип. А вот как инициируется указатель на функцию: char* (*MyPt2) (int, int, int*, float) = MyFp; Очевидно, что функция MyFp() должна быть к этому моменту не только объявлена, но и определена. Новому указателю на функцию char* (*MyPt3) (int, int, int*, float); можно также присвоить новое значение. Для этого достаточно использовать ранее определённый и проинициализированный указатель: MyPt3 = MyPt2; Или адрес ранее определённой функции: MyPt3 = MyFp; При этом инициализация и присваивание оказываются корректными лишь при условии, что имеет место точное сопоставление списков формальных параметров и списков формальных значений в объявлениях указателей и функций. |