Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
GRIGOREV.DOC
Скачиваний:
5
Добавлен:
01.07.2025
Размер:
608 Кб
Скачать
☆

1.11. Функции

Если в таких языках, как FORTRAN и PASCAL, делается различие между подпрограммами-процедурами и подпрограммами-функциями, то в языках С и С++ используются только функции. Программы на языке С (C++) - это совокупность следующих одна за другой функций, одна из которых main (главная функция). Функция main обеспечивает создание точки входа в откомпилированную программу. Каждая функция имеет статическую продолжительность существования и внешний тип компоновки: в С запрещено вложение одной функции в другую.

Функция в языке С состоит из заголовка функции и тела функции. Тело функции - это блок (последовательность операторов в {}), а заголовок в общем случае имеет вид:

тип функции имя функции ( тип параметра имя параметра ,

[ [ тип параметра имя параметра , ... ] )

Заголовок функции не является оператором, поэтому он не заканчивается “;”. Функция может иметь скалярный тип (число, указатель) или тип void (не возвращает никакого значения). Все программные единицы, не вычисляющие какого-либо одного скалярного значения, являются функциями типа void. Каждый параметр в заголовке функции описывается отдельно, даже если параметры имеют один и тот же тип. Допускаются функции без параметров, в этом случае список параметров имеет вид () или (void) .

Выход из функции осуществляется оператором

return [ выражение ] ;

Если выражение задано в операторе return, то его значение преобразуется к типу функции, в противном случае значение, возвращаемое функцией, остается неопределенным. Функция может содержать любое количество операторов return. Присваивать имени функции какое-либо значение, как в языках PASCAL и FORTRAN, не нужно.

В большинстве языков программирования параметры передаются либо по значению, либо по адресу. В первом случае подпрограмме доступна не сама переменная, а только ее значение, во втором же случае подпрограмма работает непосредственно с переменной, переданной ей в качестве параметра. Таким образом, переменную, переданную по адресу, подпрограмма может модифицировать, а переданную по значению - нет. В С++, как и в Паскале, реализованы оба способа передачи параметров. В языке С параметры передаются только по значению, функция получает лишь их значение, но не адрес, и поэтому не может изменить значение аргумента. Общепринятый способ обеспечить функции доступ к какой-либо переменной вызвавшей программы состоит в том, что вместо самой переменной в качестве параметра передается указатель на нее.

Параметры-массивы допускаются, но в качестве аргумента, соответствующего такому параметру, задается имя массива, т.е. адрес, таким образом параметр-массив есть на самом деле параметр-указатель. Для параметров-одномерных массивов нет необходимости указывать длину. Запишем, например, функцию, вычисляющую наибольший элемент массива целых чисел :

int max_el(int x[],int n) {int i,m;

for(m=x[0],i=0;i<n;i++) m=(x[i]>m)?x[i]:m; return m;}

Такая функция может обрабатывать массивы любой длины, но при каждом вызове следует определять фактическую длину с помощью аргумента n. Описание параметра int x[] практически эквивалентно описанию int* x, так что в функции можно отказаться от использования индексов, которые введены лишь для удобства записи :

int max_el(int* x,int n) {int i,m;

for(m=*x,i=1;i<n;i++) m=(*(x+i)>m)?*(x+i):m; return m;}

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

#define SIZE 3

void main(void) { int b[][SIZE]={1,2,3,4,5,6,7,8,9};

Тогда соответствующую функцию можно записать в трех вариантах :

1)

int max_el(int x[][SIZE],int n) {int i,j,m;

for(m=x[0][0],i=0;i<n;i++)for(j=0;j<n;j++) m=(x[i][j]>m)?x[i][j]:m;

return m;}

2)

int max_el(int x[][SIZE],int n) {int i,j,m;

for(m=*x[0],i=0;i<n;i++)

for(j=0;j<n;j++) m=(*(x[i]+j)>m)?*(x[i]+j):m;

return m;}

3)

int max_el(int*x,int size,int n) {int i,j,m;

for(m=*x,i=0;i<n;i++)

for(j=0;j<n;j++) m=(*(x+i*size+j)>m)?*(x+i*size+j):m;

return m;}

Вызов функции будет соответственно иметь вид:

1) max_el(b,SIZE)

2) max_el(b,SIZE)

3) max_el((int*)b,SIZE,SIZE)

Все функции в С являются рекурсивными, т.е. любая функция может вызвать любую функцию, в том числе и функцию main, а также саму себя.

В вызывающей функции (или вне функций) функцию можно описать как

тип имя ( список параметров ) ;

или

тип имя ( список типов параметров ) ;

Первый вариант описания отличается от заголовка функции только наличием “;” и называется прототипом функции, имена параметров могут быть произвольными, т.к. они никак не используются компилятором. Во втором варианте эти имена вообще опущены, например:

int max_el(int*a,int b,int c);

int max_el(int*,int,int);

Для полного описания функций, не имеющих параметров, список типов задается в виде (void) . Функции, расположенные в том же файле, до их вызова можно вообще не описывать, описанием будет служить заголовок функции. Если функция расположена после вызывающей функции или в другом файле, то ее можно описывать или не описывать. Но для неописанной функции компилятор не проверяет соответствие типов параметров и аргументов и соответствие количества параметров и аргументов, кроме того, тип неописанной функции всегда полагается равным int. Если функция описана, компилятор следит за правильностью списка аргументов.

Язык С++ не предусматривает автоматического преобразования в тех случаях, когда аргументы не совпадают по типам с соответствующими им параметрам. С++ требует, чтобы в модуле, где происходит обращение к функции (причем обязательно до соответствующего обращения) присутствовало либо определение этой функции, либо ее прототип. Таким образом, проверка соответствия типов параметров и аргументов всегда выполняется в С++ на этапе компиляции.

В С++ тип функции столь же важен, как и ее имя. Две функции могут иметь одинаковое имя, если имеется возможность различить их по сигнатуре. Сигнатура функции задается числом, порядком следования и типами ее параметров. Определение двух или более функций с одним и тем же именем называется перегрузкой функций, поскольку более поздние объявления перегружают, или перекрывают, более ранние. Рассмотрим следующую программу:

#include <stdio.h>

#include <string.h>

int func (int first){ return first*first;}

int func (unsigned first){ return -first*first;}

char func (char first){ return first+3;}

float func(float r){ return r*r;}

void main() { printf("%d ",func(4)); printf("%d ",func((unsigned)4));

printf("%c ",func('a')); printf("%f\n",func((float)1.2));}

Эта программа, в которой использованы четыре разные функции с именем func, является совершенно корректной и выведет на экран строку

16 -16 d 1.44

В языке С допускаются указатели на функции, им можно присвоить адрес функции и вызвать функцию по адресу, например:

int (*p)(int*,int,int)=&max_el; printf("%d",(*p)((int*)b,SIZE,SIZE));

Операции адрес & и значение * можно не записывать явно, они автоматически генерируются компилятором, т.е. приведенный выше фрагмент можно переписать в виде:

int (*p)(int*,int,int)=max_el; printf("%d",p((int*)b,SIZE,SIZE));

Используя указатели на функции, можно решать два типа задач:

1) присвоение указателю функции и использование указателя в качестве косвенной ссылки на функцию;

2) передача функций в качестве аргументов для других функций. Это позволяет создавать мощную систему функций, где частью функции является другая функция, выбранная из библиотеки подпрограмм.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]