Тип_возвращаемого_значения имя_функции(список_параметров)
{
Тело функции
}
Тип_возвращаемого_значения определяет тип переменной, которую возвращает функция.
- спецификатор класса памяти – задает класс памяти функции, который может быть или static или extern;
- спецификатор типа и декларатор определяют тип возвращаемого значения и имя функции;
- список параметров - это список (возможно пустой) формальных параметров, которые используются функцией;
- объявления параметров задают типы формальных параметров;
- тело функции - это составной оператор, содержащий объявления локальных переменных и операторы.
Класс памяти
Спецификатор класса памяти в определении функции определяет функцию как static или extern. Функция с классом памяти static видима только в том исходном файле, в котором она определена. Все другие функции с классом памяти extern, заданным явно или неявно, видимы во всех исходных файлах, которые образуют программу. Если спецификатор класса памяти опускается в определении функции, то подразумевается класс памяти extern.
Тип возвращаемого значения
Спецификатор типа вместе с декларатором определяет тип возвращаемого значения и имя функции. Если не задан, то подразумевается, что тип int. Функции не могут возвращать массивов или функции, но они могут возвращать указатели на любой тип, включая массивы и функции. Тип используется только тогда, когда функция возвращает значение, которое вырабатывается, если выполняется оператор return, содержащий выражение. Выражение вычисляется, преобразуется к типу возврата, если это необходимо, и возвращается в точку вызова. Если оператор return не выполняется или если выполняемый оператор return не содержит выражения, то значение возврата функции не определено. Если в этом случае вызывающая функция ожидает значение возврата, то поведение программы также не определено. /*
Пример
*/ int add (int x,int y)
{
return (x+y);
}
/*
Формальные параметры
Формальные параметры – это переменные, которые принимают значения, переданные функции в точке вызова. Формальные параметры объявляются в списке параметров заголовка и заключаются в круглые скобки. Список параметров определяет имена параметров и порядок, в котором они принимают значения при вызове. Разделяются параметры в списке запятой. Тип каждого формального параметра должен соответствовать типу фактического аргумента.
Тело функции
Тело функции – это просто составной оператор. Составной оператор содержит операторы, которые определяют действия функции, и может также содержать объявления переменных, используемых в этих операторах. Все переменные, объявленные в теле функции, имеют тип памяти auto, если они не объявлены иначе. Когда вызывается функция, то создается память для локальных переменных и производится их инициализация (если она задана). Управление передается первому оператору составного оператора и начинается процесс выполнения, который продолжается до тех пор, пока не встретится оператор return или конец тела функции. Управление при этом возвращается в точку вызова. Если функция возвращает значение, то должен быть выполнен оператор return, содержащий выражение. Значение возврата не определено, если не выполнен оператор return или если в оператор return не было включено выражение.
Фактические параметры
Фактическим параметром мы называем конкретное значение, передаваемое функции в момент вызова. В качестве них могут выступать константы или вычисляемые выражения. Все фактические аргументы передаются по значению. Копия фактических аргументов присваивается соответствующим формальным параметрам. Функция использует эти копии, не влияя на переменные, с которых копия была сделана. Путь доступа из функции к значениям оригинальных переменных обеспечивают указатели, т.к. указатель на переменную содержит адрес переменной, то функция может использовать этот адрес для доступа к значению переменной. Аргументы-указатели обеспечивают доступ из функции к массивам и функциям, которые запрещено передавать как аргументы. Несоответствие типов формальных и фактических параметров может привести к ошибкам, особенно когда несоответствие влечет за собой отличия в размерах. В этом случае могут происходить неявные преобразования, потеря точности и т.д.
Пример:
void swap (int *a, int *b);
{
int t;
t=*a;
*a=*b;
*b=t;
}
void main()
{
int x=5, y=6;
swap (&x,&y);
....
}
В примере функция swap объявлена как функция с двумя аргументами типа указателей на int. Формальные параметры a и b объявлены так же, как указатели на целые величины. При вызове функции адрес x запоминается в a и адрес y запоминается в b. Теперь два имени или "синонима" существует для одной и той же ячейки памяти. Ссылки *a и *b в функции swap действуют точно так же, как x и y в main. Присваивание внутри функции swap изменяет содержимое x и y.
Рекурсивные вызовы
Любая функция в С++ может быть вызвана рекурсивно. Для этого функция вызывает саму себя. Компилятор С++ допускает любое число рекурсивных вызовов, однако реально оно ограничивается размером стековой памяти. При каждом вызове формальных параметров и локальных переменных захватывается новая память, так что их значения из предшествующих незавершенных вызовов не перезаписываются. Предшествующие параметры недоступны в других версиях функции, исключая версию, в которой они были созданы. Заметим, что переменные, объявленные как глобальные, не требуют новой памяти при каждом рекурсивном вызове. Их память сохраняется на все время жизни программы.
Передача параметров
Передача параметров в функцию может осуществляться двумя способами: по ссылке и по значению. В отличие от других языков, например Visual Basic, в С++ по умолчанию происходит передача по значению. Это означает, что в функции создается локальная переменная, в которую копируется значение из внешней переменной.
Пример:
void fun(int a)
{
a=10; // присваиваем локальной переменной
}
void main()
{
int x=20;
fun(x); // не изменяем значение x
...
}
Для того, чтобы функция могла присвоить значение внешней переменной, ей нужно передать адрес этой ячейки памяти.
Пример:
void fun(int *a)
{
*a=10; // присваиваем переменной x новое значение
}
void main()
{
int x=20;
fun(&x); // изменяем значение x
...
}
При передаче в функцию массива, всегда реально передается адрес первого элемента, так что массивы передаются всегда по ссылке.
Например:
void fun(int *a,int N)
{
for(int i=0; i<N; i++)
a[i]=i*i; // заполняем массив
}
void main()
{
int x[10];
fun(x,10);
...
}
Если функция должна обработать динамический двумерный массив, то в качестве формального параметра может выступать указатель на указатель, а в качестве фактического – имя массива.