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

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

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

112

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

Пример той же функции:

double f (int a, float b, double d)

(/♦тело функции*/}

Принципиально важным оператором тела функции является оператор возврата из функции в точку ее вызова:

return выражение;

или

return;

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

Применение оператора return допустимо и в функции main( ). Если программист явно не поместил в функцию main( ) оператор return, то компилятор поместит его в конце текста функции main(). В отличие от "неглавных" функций, откуда возврат выполняется в вызывающую функцию, выполнение оператора return; или return выражение; в функции m ain() приводит к завершению программы. Управление при таком вы­ ходе передается вызывающей программе, например операцион­ ной системе, которая может анализировать значение выражения, использованного в операторе возврата.

Приведем примеры определения функций и проиллюстриру­ ем некоторые их особенности.

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

Пусть g и h - параметры; тогда объем цилиндра равен g*g*h*PI, если g>h, или g*h*h*PI, если g<h, где PI - число я.

Текст нестандартного (старомодного) определения функции:

Глава 2. Введение в программирование на языке Си

113

float volume

(g,h)

 

float g,h;

 

 

i

,

 

float b; /* b - вспомогательная переменная */ if ( g < h)

{

b=h;

h=g;

g=b;

>

return 3.14159*g*g*h;

>

Первые две строки — заголовок функции с именем volume; далее - тело функции. Параметры специфицированы как веще­ ственные типа float. Для возврата из функции и передачи ре­ зультата в точку вызова в теле функции используется оператор

return 3.14159*g*g*h;

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

float w(float g, float h)

{

if ( g >= h )

return 3.14159*g*g*h; else

return 3.14159*g*h*h;

)

Функция для вычисления скалярного произведения век­ торов. Скалярное произведение двух векторов n-мерного ли­ нейного пространства вычисляется по формуле

(=1

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

8~3124

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

/* Скалярное произведение п-мерных векторов */ float S_Product(n,a,b)

int n;

/* Размерность пространства векторов */

float а[

],b[

]; /* Массивы координат векторов*/

{ int i ;

/*

Параметр цикла */

float z;

/*

Формируемая сумма */

for (i=0, z=0.0; i<n; i++)

 

z+=a[i]*b[i];

return z;

/* Возвращаемый результат */

)

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

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

Современный формат (введенный стандартом) предусматри­ вает такую запись заголовка:

float ScalarProduct (int n, float a[ ], float b[ ])

Тело функции остается одинаковым при обоих форматах оп­ ределения заголовка.

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

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

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

Глава 2. Введение в программирование на языке Си

115

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

volume(3.05,х*2) w(z-l.0,1е-2)

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

типрезультата имя_функции();

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

float volume();...

... z=volume(z-1.0,le-2);

Переменная z получит вещественное значение (объем цилин­ дра), вычисленное с помощью функции volume( ). Если описа­ ние float volume(); опустить, то в вызывающей программе 8*

116

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

функция

volume() воспринимается как целочисленная, т.е.

результат будет неверен.

Стандарт языка Си предусматривает обязательное описание функции со стандартным заголовком с помощью прототипа. Прототип в отличие от старомодного описания включает спе­ цификацию параметров:

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

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

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

float w(float, float);

Scalar_Product ( int n, float a[ ], float b[ ]);

Имена формальных параметров в прототипе функции w () не указаны, специфицированы только их типы.

Отметим, что применение прототипа предполагает только стандартную форму записи заголовка функции. Использование прототипа несовместимо со "старомодным" видом заголовка. Мы настоятельно рекомендуем стандартный вид заголовка и применение прототипов.

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

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

Глава 2. Введение в программирование на языке Си

117

Вычисление биномиального коэффициента. Как известно,

/и!(л-/и)!’

где n > m > 0; n, m - целые.

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

#include <stdio.h>

int fact(int k) /* Вычисление факториала k!*/

{

int j, i; /* Вспомогательные переменные */ for(i=l, j=l; i<=k; i++) /*Цикл вычисления*/

j*=i; return j ;

}/* Конец определения функции */

/* Вычисление биномиального коэффициента: */ void main( )

{

int п, m, nmc, nm; /*nm - значение (n-m) */ /* nmc - значение биномиального коэффициента */ while (1)

{

printf("\пВведите n=") ; scanf("%d",&n); printf("Введите m="); scanf("%d", &m);

if (m>=0 && n>=m && n<10) break; printf("Ошибка• Необходимо 0<=т<=п<1б");

)

ГШ= П “ Шt

nmc=fact(п)/fact(т)/fact(пт);

printf ("\n Биномиальный коэффициент=%б", nmc); ) /* Конец основной программы */

В основной программе функция fact() не описана. Ее прото­ тип здесь не нужен, так как определение функции находится в том же файле, что и функция m ain(), вызывающая fact(), при­ чем определение размещено выше вызова. Пример выполнения программы:

118

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

Введите п=4 Введите т=5

Ошибка ! Необходимо 0<т<=п<10 Введите п=4 Введите ш=2

Биномиальный коэффициент =6

Вычисление объема цилиндра с использованием приведен­ ной выше функции w ():

#include <stdio.h>

/* Вычисление объема цилиндра: */ void main( )

{

float w(float, float); /* Прототип функции */

float a,b;

/* Исходные данные

*/

int

j;

/* Счетчик попыток

ввода

*/

for

(j=0; j<5; j++)

*/

 

{

/* Цикл

ввода данных

 

printf("\n Введите a=") ;

 

 

scanf("%f",&a);

 

 

printf("

Введите b=");

 

 

 

scanf("%f",fib);

break;

 

if

( a > 0 . 0 & & b > 0 . 0 )

printf("\n Ошибка, нужно

a>0 и b>0!\n");

)

if (j == 5)

{

printf("\n Оч2НЬ ПЛОХО вводите данные!!"); return; /* Аварийное окончание программы*/

)

printf("\n Об'ьем цилиндра =%f", w(a,b));

}/* Конец основной программы */

/* Функция для вычисления об'ьема цилиндра: */ float w(float g, float h)

{

if ( g >= h ) return(3.14159*g*g*h);

else return(3.I4159*g*h*h);

)

Глава 2. Введение в программирование на языке Си

119

В основной программе использован оператор return, преры­ вающий исполнение программы. Оператор return выполняется после цикла ввода исходных данных, если количество неудач­ ных попыток ввода (значений а и Ь) равно 5. Задан прототип функции w( ), т.е. задан ее прототип, что необходимо, так как она возвращает значение, отличное от int, и определена стан­ дартным образом позже (ниже), чем обращение к ней. Обраще­ ние к функции w () использовано в качестве фактического параметра функции printf( ).

Пример выполнения программы;

Введите а=2.О Введите Ь=-44.3

Ошибка, нужно а>0 и Ь>0 Введите а=2.0 Введите Ь=3.0

Объем цилиндра=56.548520

Вычисление площади треугольника. Для определения площади треугольника по формуле Герона

S = J p ( p - A ) ( p - B X p - C )

достаточно задать длины его сторон А, В, С и, вычислив полупериметр р=(А+В+С)/2, вычислить значение площади по фор­ муле. Однако для составления соответствующей программы необходима функция вычисления квадратного корня. Предпо­ ложив, что такой функции в библиотеке стандартных математи­ ческих функций нет, составим ее сами. В основу положим метод Ньютона:

где z - подкоренное выражение; х0 - начальное приближение. Вычисления будем проводить с фиксированной относитель­

ной точностью е. Для простоты условием прекращения счета

будет выполнение неравенства — — — < е. Для вычисления

абсолютного значения введем еще одну функцию с именем

120

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

abs() (хотя такая функция, так же как функция для вычисления квадратного корня, есть в стандартной библиотеке). Программа может быть такой:

/* Вычисление площади треугольника */

#include <stdio.h> /*Для средств ввода-вывода*/ #include <stdlib.h> /* Для функции ex±t( ) */ void main( )

{

float a,b,c,p,s;

float sqr(float); /* Прототип функции */ printf("\n Сторона a= ") ; scanf("%f",&a); / printf("Сторона b= ");

scanf("%f",&b); printf("Сторона c= "); scanf("%f",&c);

if(a+b <= с II a+c <= b || b+c <= a)

{

printf("\n Треугольник построить нельзя!"); return; /* Аварийное окончание работы */

)

p=(a+b+c)/2; /* Полупериметр */ s=sqr(р*(р-а)* (р-Ь)* (р-с)); printf("Площадь треугольника: %f",s);

}/* Конец основной программы */

/* Определение функции вычисления квадратного корня */

float sqr(float х)

{/* х-подкоренное выражение */ /♦Прототип функции вычисления модуля: */

float abs(float);

double

г ,q ;

REL=0.00001;

const double

if (x <

/*

REL-относительная точность */

0.0)

'

{

 

printf("\n

Отрицательное подкоренное"

" выражение");

exit(l); /* Аварийное окончание.программы */

}

if (х = 0.0) return х ;

Глава 2. Введение в программирование на языке Си

121

/* Итерации

вычисления корня: */

*/

г=х;

/*

г - очередное приближение

do {

 

 

 

q=r; /* q - предыдущее приближение */ r=(q+x/q)/2;

>

while (abs((r-q)/г) > REL); return r;

}/* Конец определения функции sqr */

/* Определение функции */ /* для получения абсолютного значения: */ float abs(float z)

{

if(z > 0) return z; else return(-z);

}/* Конец определения функции abs */

Впрограмме используются три функции. Основная функция main( ) вызывает функцию sqr( ), прототип которой размещен выше вызова. Функция abs( ) не описана в основной программе, так как здесь к ней нет явных обращений. Функция abs() вызы­ вается из функции sqr(), поэтому ее прототип помещен в тело функции sqr().

Впроцессе выполнения программы может возникнуть ава­ рийная ситуация, когда введены такие значения переменных а, Ь, с, при которых они не могут быть длинами сторон одного треугольника. При обнаружении такой ситуации выдается пре­ дупреждающее сообщение "Треугольник построить нельзя!" и основная функция main() завершается оператором return. В функции sqr() также есть защита от неверных исходных дан­ ных. В случае отрицательного значения подкоренного выраже­ ния (х) нужно не только прервать вычисление значения корня, но и завершить выполнение программы с соответствующим предупреждающим сообщением. Оператор return для этого не­ удобен, так как позволяет выйти только из той функции, в кото­ рой он выполнен. Поэтому вместо return; при. отрицательном значении х в функции sqr( ) вызывается стандартная библиотеч­ ная функция exit(), прекращающая выполнение программы. Прототип (описание) функции exit( ) находится в заголовочном

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