C++. Бархатный путь. Часть 1 - Тип функции
ОГЛАВЛЕНИЕ
Тип функции
Основными характеристиками функции является тип возвращаемого значения и список типов формальных параметров. Подобно тому, как имена переменных никаким образом не влияют на их тип, имена функций не является частью их типа. Тип функции определяется типом возвращаемого значения и списком типов её формальных параметров. Например, пара функций
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;
При этом инициализация и присваивание оказываются корректными лишь при условии, что имеет место точное сопоставление списков формальных параметров и списков формальных значений в объявлениях указателей и функций.