Возврат значений функциями
Функция может возвращать любое значение, но только единственное, это особенность использования функций в языке С. При необходимости изменения функцией нескольких значений как уже было сказано используется не возвращаемое значение, а использование в качестве аргументов функции указателей и ссылок. Но функция может не только принимать указатели и ссылки в качестве параметров она так же может их возвращать.
Вернуть значение указателя легко. Значение указателя – это просто адрес, поэтому если программист захочет вернуть адрес некоторой переменной value, то можете просто записать так:
return &value; // Возвращение адреса
До тех пор, пока заголовок функции и ее прототип правильно и согласованно указывают тип возврата, у программы нет никаких проблем, по крайней мере, никаких видимых проблем. Предполагая, что переменная value имеет тип double, прототип функции по имени treble, которая может содержать приведенный выше оператор return, должен быть таким:
double* treble(double data);
Давайте рассмотрим функцию, возвращающую указатель. Предположим, что нужна функция, которая возвращает указатель на местоположение в памяти, где находится значение ее аргумента, умноженное на 3.
#include <iostream>
using namespace std;
double* treble(double data);
double* treble(double data)
{
double result = 0.0;
result = 3.0*data;
return &result;
}
int main(void)
{
double d;
double *ps;
cout<<"Vvedite d :\t";
cin>>d;
ps=treble(d);
cout<<"s="<<* ps <<endl;
return 0;
}
Если формально подходить к коду программы, то она не должна работать корректно, так как под указатель ps не выделена память и он ссылается на «мусор». В строке ps=treble(d); значение адреса принимает адрес локальной переменной из функции, которая прекратила свою работу, то есть в оперативной памяти на этом месте появилась отметка «свободна». Память может быть выделена под любой друглй процесс и поэтому сохранение правильного значения здесь не гарантируется. Поэтому указатель может быть возвращен в вызываемую функцию в двух случаях: если память была выделена динамически или указатель быть инициализирован не в возвращающей его функции.
Функция может возвращать ссылку. Это также чревато потенциальными ошибками, как и в случае возврата указателя, поэтому здесь необходимо также соблюдать осторожность. Поскольку ссылка не является какой-то отдельной сущностью (это всегда псевдоним чего-то другого), программист должен быть уверен, что объект, на который она ссылается, все еще существует после завершения выполнения функции. Очень легко забыть об этом, используя в функции ссылки, поскольку они выглядят как обычные переменные.
Ссылки, как возвращаемые типы, особенно важны в контексте объектно-ориентированного программирования. В этом случае они позволяют делать такие вещи, которые невозможно осуществить без их применения (в частности, это касается "перегрузки операций"). Принципиальная характеристика возвращаемого значения типа ссылки в том, что оно является lvalue. Это значит, что его можно использовать как результат функции, которая возвращает ссылку, в левой части оператор присваивания.
Общее правило применимое к возврату указателей и ссылок будет таким: Никогда не возвращать из функции ссылку или адрес на локальную переменную.
В качестве параметров функции может быть не только простые значения, указатели и ссылки, но и функции. Естественно, что передать функцию как параметр можно, только если использовать указатель на функцию.
Указатель сохраняет значение адреса, и все рассмотренные до настоящего времени указатели хранили адреса других переменных с тем же базовым типом, что и конкретный указатель. Это обеспечивало относительную гибкость в смысле того, что можно было обращаться в разное время к разным переменным через один и тот же указатель. Но указатель также может хранить адрес функции. Это дает возможность вызывать функцию через указатель, адрес которой был присвоен данному указателю. Очевидно, что указатель на функцию должен содержать адрес памяти, где находится функция, которую необходимо вызвать. Чтобы работать правильно, однако, такой указатель должен поддерживать информацию о списке параметров соответствующей функции и типе ее возвращаемого значения. Таким образом, когда объявляется указатель на функцию, то в дополнение к имени указателя должны быть специфицированы типы параметров и тип возврата функции, на которую он может указывать. Ясно, что это необходимо для того, чтобы ограничить то, что можно присваивать каждому конкретному указателю функции.
Объявление указателя pfun, который может указывать на функцию, принимающую два аргумента типа float и float и возвращающую значение типа float. Объявление должно выглядеть так:
float (*pfun)( float, float); // Объявление указателя на функцию
Следует обратить внимание на то, что скобки выглядят несколько загадочно. Этот оператор объявляет указатель по имени pfun, который может указывать на функции, принимающие два аргумента – типа float и возвращающие значение типа float. Скобки вокруг имени указателя pfun, как и звездочка – необходимы; без них этот оператор было бы просто объявлением функции, а не объявлением указателя. В этом случае оно выглядело бы так:
float *pfun(float, float); // Прототип функции
// возвращающей тип float *
Такой оператор является прототипом функции pfun (), принимающей два параметра и возвращающей указатель на значение типа float. Поскольку требуется объявить указатель, то прототип – это совсем не то, что нужно в данный момент.
Общая форма объявления указателя на функцию выглядит так:
тип_возврата (*имя_указателя) (список_типов_параметров);
Указатель может указывать только на функции с тем же типом возврата и списком типов параметров, что заданы в его объявлении.
Это показывает, что объявление указателя на функцию состоит из трех перечисленных ниже компонентов.