книги / Программирование на языке Си
..pdf112 |
Программирование на языке Си |
Пример той же функции:
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( ) находится в заголовочном