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;

При этом инициализация и присваивание оказываются корректными лишь при условии, что имеет место точное сопоставление списков формальных параметров и списков формальных значений в объявлениях указателей и функций.