Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги / Программирование на языке Си

..pdf
Скачиваний:
15
Добавлен:
12.11.2023
Размер:
17.16 Mб
Скачать

202

Программирование на языке Си

int i,n; n=sizeof(point)/sizeof(point[0]);

for(i=0;i<n;i++) printf("\n%s",point[i] ) i

)

Результат выполнения программы:

thirteen fourteen

fifteen sixteen seventeen

eighteen nineteen

Глава 5

ФУНКЦИИ

5.1. О бщ ие сведения о ф ункциях

Определение функции. В соответствии с синтаксисом в языке Си определены три производных типа: массив, указатель, функция. В этой главе рассмотрим функции.

О функциях в языке Си нужно говорить, рассматривая это понятие с двух сторон. Во-первых, функция, как мы только что сказали, - это один из производных типов (наряду с массивом и указателем). С другой стороны, функция - это минимальный исполняемый модуль программы на языке Си. Синонимами это­ го второго понятия в других языках программирования являют­ ся процедуры, подпрограммы, подпрограммы-функции, про­ цедуры-функции. Все функции в языке Си имеют рекомендуе­ мый стандартами языка единый формат определения:

тип имяфункции (спецификация параметров) тепофункции

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

Здесь тип либо void (для функций, не возвращающих значе­ ния), либо обозначение типа возвращаемого функцией значения. В предыдущих главах рассмотрены функции, возвращающие значения базовых типов (char, int, double и т.д.).

Имя функции либо main - для основной (главной) функции программы, либо произвольно выбираемое программистом имя (идентификатор), не совпадающее со служебными словами и с именами других объектов (и функций) программы.

Спецификация параметров - это либо пусто, либо список формальных параметров, каждый элемент которого имеет вид:

204

Программирование на языке Си

обозначение типа имя параметра

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

int printf (const char * format,...); int scanf (const char * format,...);

Указанные функции форматированного вывода и формати­ рованного ввода позволяют применять теоретически неогра­ ниченное количество фактических параметров. Обязательным является только параметр char * format - "форматная строка", внутри которой с помощью спецификаций преобразования оп­ ределяется реальное количество параметров, участвующих в обменах.

Телофункции - это часть определения функции, ограничен­ ная фигурными скобками и непосредственно размещенная вслед за заголовком функции. Тело функции может быть либо состав­ ным оператором, либо блоком. Напоминаем, что в отличие от составного оператора блок включает определения объектов (пе­ ременных, массивов и т.д.). Особенность языка Си состоит в невозможности внутри тела функции определить другую функ­ цию. Другими словами, определения функций не могут быть вложенными.

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

Глава 5. Функции

205

return;

return выражение;

Первая форма соответствует завершению функции, не воз­ вращающей никакого значения, т.е. функции, перед именем ко­ торой в ее определении указан тип void. Выражение во второй форме оператора return должно иметь тип, указанный перед именем функции в ее определении, либо иметь тип, допускаю­ щий автоматическое преобразование к типу возвращаемого функцией значения.

Мы отметили обязательность оператора возврата из тела функции, однако оператор return программист может явно не использовать в теле функции, возвращающей значение типа void (ничего не возвращающей). В этом случае компилятор ав­ томатически добавляет оператор return в конец тела функции перед закрывающейся фигурной скобкой "}".

Итак, в языке Си допустимы функции с параметрами и без параметров, функции, возвращающие значения указанного типа и ничего не возвращающие.

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

тип имяфункции (списокпараметров) спецификация параметров; телофункции

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

double f

(int n,

float x)

double

f

(n, x)

{

функции

*/

int

n;

x;

 

/* Тело

float

 

}

 

 

{

Тело

функции */

 

 

 

/*

 

 

 

}

 

 

 

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

206

Программирование на языке Си

Описание функции и ее тип. Для корректного обращения к функции сведения о ней должны быть известны компилятору, т.е. до вызова функции в том же файле стандартом рекоменду­ ется помещать ее определение или описание. Двум формам (стандартной и старомодной) определения функции соответст­ вуют две формы их описания. Для функции, определенной стандартным образом, описанием служит ее прототип:

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

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

double f (int n, float x); double f (int, float);

При старом формате определения функции используется ста­ ромодный способ описания функции, не содержащий специфи­ кации параметров:

тип имя фупкции ();

Например, для некоторой функции old() в программе может быть использовано следующее описание:

double oldf( );

При таком описании функции компилятор ничего "не знает" о типах ее параметров и не сможет выполнить проверку совмес­ тимости списков фактических и формальных параметров. По­ этому рекомендуется для описания функции всегда применять ее прототип, что возможно только при стандартном определе­ нии функции.

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

Глава 5. Функции

207

double f (int, float); void w( );

Тип функции f ( ) - это функция, возвращающая значения ти­ па double и имеющая параметры типов int и float. Тип функции w () (вводимый старомодным описанием) опознать практически невозможно. Служебное слово void определяет только тип воз­ вращаемого значения. Таким образом, w () - это функция, не возвращающая значения и имеющая неизвестное количество параметров неизвестного типа. Именно поэтому при определе­ нии типа "функция" нужно всегда использовать прототип, а не старомодное описание.

Вызов функции. Для обращения к функции используется выражение с операцией "круглые скобки":

обозначение функции (список фактических параметров)

Операндами операции '()' служат обозначение функции и список фактических параметров. Наиболее естественное и понятное обозначение функции - это ее имя. Кроме того, функ­ цию можно обозначить, разыменовав указатель на нее. Так как указатели на функции мы еще не ввели (см. следующие пара­ графы этой главы), то ограничимся пока для обозначения функ­ ций их именами.

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

Между формальными и фактическими параметрами должно быть соответствие по типам. Лучше всего, когда тип фактиче­ ского параметра совпадает с типом формального параметра. В противном случае компилятор автоматически добавляет коман­ ды преобразования типов, что возможно только в том случае,

208

Программирование на языке Си

если такое приведение типов допустимо. Например, пусть опре­ делена функция с прототипом:

int g(int, long);

Далее в программе использован вызов:

g(3.0+m, 6.4е+2)

Оба фактических параметра в этом вызове имеют тип double. Компилятор, ориентируясь на прототип функции, автоматиче­ ски предусмотрит такие преобразования:

g ((int)(3.0+m), (long) 6.4e+2)

Так как вызов функции является выражением, то после вы­ полнения операторов тела функции в точку вызова возвращает­ ся некоторое значение, тип которого строго соответствует типу, указанному перед именем функции в ее определении (и прото­ типе). Например, функция

float ft(double х, int n)

{

if (x<n) return x; return n ;

}

всегда возвращает значение типа float. В выражения, помещен­ ные в операторы return, компилятор автоматически добавит средства для приведения типов, т.е. получим (невидимые про­ граммисту) операторы:

return (float) х; return (float) n;

Особое внимание, нужно уделить правилам передачи пара­ метров при обращении к функциям. Синтаксис языка Си преду­ сматривает только один способ передачи параметров - передачу по значениям. Это означает, что формальные параметры функ­ ции локализованы в ней, т.е. недоступны вне определения функции и никакие операции над формальными параметрами в теле функции не изменяют значений фактических параметров.

Глава 5. Функции

209

Передача параметров по значению предусматривает сле­ дующие шаги:

1.При компиляции функции (точнее, при подготовке к ее выполнению) выделяются участки памяти для формаль­ ных параметров, т.е. формальные параметры оказываются внутренними объектами функции. При этом для парамет­ ров типа float формируются объекты типа double, а для параметров типов char и short int создаются объекты типа int. Если параметром является массив, то формируется указатель на начало этого массива и он служит представ­ лением массива-параметра в теле функции.

2.Вычисляются значения выражений, использованных в ка­ честве фактических параметров при вызове функции.

3.Значения выражений - фактических параметров заносятся в участки памяти, выделенные для формальных парамет­ ров функции. При этом float преобразуется в double, а

char и short int - в тип int (см. п. 1).

4.В теле функции выполняется обработка с использованием значений внутренних объектов-параметров, и результат передается в точку вызова функции как возвращаемое ею значение.

5.Никакого влияния на фактические параметры (на их зна­ чения) функция не оказывает.

6.После выхода из функции освобождается память, выделен­ ная для ее формальных параметров.

Вызов функции всегда является выражением, однако разме­ щение такого выражения в тексте программы зависит от типа возвращаемого функцией значения. Если в качестве типа воз­ вращаемого значения указан тип void, то функция является функцией без возвращаемого результата. Такая функция не мо­ жет входить ни в какие выражения, требующие значения, а должна вызываться в виде отдельного выражения-оператора:

имя функции (список фактических параметров);

14~3124

210

Программирование на языке Си

Например, следующая функция возвращает значение типа void:

void print(int gg, int mm, int dd)

{

printf("\n год: %d",gg); printf(",\t месяц: %d,",mm); printf(",\t день: %d.*', dd) ;

}

Обращение к ней

print(1966, 11, 22);

приведет к такому выводу на экран:

год: 1966, месяц: 11, день: 22,

Может оказаться полезной и функция, которая не только не возвращает никакого значения (имеет возвращаемое значение типа void), но и не имеет параметров. Например, такая:

#include <stdio.h> void Real_Time (void)

{

printf("\n Текущее время: %s", __ TIME__ " (час: мин: сек.)");

}

При обращении

Real_Time ( );

в результате выполнения функции будет выведено на экран дисплея сообщение:

Текущее время: 14:16:25 (час: мин: сек.)

5.2. У казатели в парам етрах ф ункций

Указатель-параметр. В предыдущем параграфе мы доста­ точно подробно рассмотрели механизм передачи параметров при вызове функций. Схема передачи параметров по значениям не оставляет никаких надежд на возможность непосредственно изменить фактический параметр за счет выполнения операторов

Глава 5. Функции

211

тела функции. И это действительно так. Объект вызывающей программы, использованный в качестве фактического парамет­ ра, не может быть изменен из тела функции. Однако существует косвенная возможность изменять значения объектов вызываю­ щей программы действиями в вызванной функции. Эту возмож­ ность обеспечивает аппарат указателей. С помощью указателя в вызываемую функцию можно передать адрес любого объекта из вызывающей программы. С помощью выполняемого в тексте функции разыменования указателя мы получаем доступ к адре­ суемому указателем объекту из вызывающей программы.

Тем самым, не изменяя самого параметра (указатель-пара- метр постоянно содержит только адрес одного и того же объек­ та), можно изменять объект вызывающей программы.

Продемонстрируем изложенную схему на простом примере:

#include <stdio.h>

void positive(int * m) /* Определение функции */

{

m = *m > 0 ? *m : —*m;

>

void main()

{

int k=-3; positive(&k); printf("\nk=%d", k);

}

Результат выполнения программы:

k=3

Параметр функции positive() - указатель типа int *. При об­ ращении к ней из основной программы m ain() в качестве фак­ тического параметра используется адрес &к переменной типа int. Внутри функции значение аргумента (т.е. адрес &к) "запи­ сывается" в участок памяти, выделенный для указателя int *m. Разыменование *ш обеспечивает доступ к тому участку памяти, на который в этот момент "смотрит" указатель ш. Тем самым в выражении

14*

Соседние файлы в папке книги