Главная страница

Курс на Си. Подбельский. Курс программирования на Си. В., Фомин С. С. Курс программирования на языке Си Учебник


Скачать 1.57 Mb.
НазваниеВ., Фомин С. С. Курс программирования на языке Си Учебник
АнкорКурс на Си
Дата18.02.2023
Размер1.57 Mb.
Формат файлаdocx
Имя файлаПодбельский. Курс программирования на Си.docx
ТипУчебник
#943863
страница10 из 42
1   ...   6   7   8   9   10   11   12   13   ...   42

п.

Для решения этой задачи определим функцию:

float w(float g, float h)

{

if ( g >= h )

return 3.14159*g*g*h; else

return 3.14159*g*h*h;

}

Для возврата из функции и передачи результата в точку вызова в теле функции используются два оператора return.

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

s = E«A. /=1

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

/* Скалярное произведение n-мерных векторов */

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

{ int i; /* Параметр цикла */

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

for (i=0, z=0.0; i

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

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

}

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

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

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

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

w(z-1.0,1e-2)

Стандарт языка Си предусматривает обязательное описание

функции с помощью прототипа. Прототип имеет формат:

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

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

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

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

float w(float, float);

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

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

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

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

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



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

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

#include

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

int j, i; /* Вспомогательные переменные */

for(i=1, j=1; i<=k; i++) /*Цикл вычисления*/

j*=i;

return j;

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

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

void main( )

{

int n, m, nmc, nm; /*nm - значение (n-m) */

/* nmc - значение биномиального коэффициента */

while (1)

{

printf("\nBeegume n=");

scanf("%d",&n);

printf("Beegume m=");

scanf("%d", &m);

if (m>=0 && n>=m && n<10) break;

printf("Ошибка! Необходимо 0<=m<=n<10");

}

nm=n-m;

nmc=fact(n)/fact(m)/fact(nm);

printf ("\n Биномиальный коэффициент=%б", nmc);

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

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

Введите n=4

Введите m=5

Ошибка ! Необходимо 0
Введите n=4

Введите m=2

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

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

#include

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

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",&b);

if ( a > 0.0 && b > 0.0 ) break;

printf("\n Ошибка, нужно a>0 и b>0!\n");

}

if (j == 5)

{

printf("\n ОЧЕНЬ ПЛОХО вводите данные!!");

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.14159*g*h*h);

}

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

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

Введите a=2.0



Введите b=-44.3



Ошибка, нужно a>0 и b>0




Введите a=2.0



Введите b=3.0



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




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

s = 7p(p-^)(p-b)(p-c)

достаточно задать длины его сторон А, В, С и, вычислив полупе­риметр р=(А+В+С)/2, вычислить значение площади по формуле.

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

xt = (xi-1 + z/x--1)/2, i = 1, 2, ...

где z - подкоренное выражение; x0 - начальное приближение.

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





полнение неравенства

*,-i

х,

< е. Для вычисления абсолютного

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

#include /*Для средств ввода-вывода*/

#include /* Для функции exit( ) */ 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 <= c || a+c <= b || b+c <= a)

{

printf("\n Треугольник построить нельзя!");

return; /* Аварийное окончание работы */

}

p=(a+b+c)/2; /* Полупериметр */

s=sqr(p*(p-a)*(p-b)*(p-c));

printf("Площадь треугольника: %f",s);

} /* Конец основной программы */ /* Oпределение функции вычисления квадратного корня */ float sqr(float x)

{ /* x-подкоренное выражение */

/*Прототип функции вычисления модуля: */

float abs(float);

double r,q;

const double REL=0.00001;

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

if (x < 0.0)

{

printf("\n Отрицательное подкоренное"

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

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

if (x == 0.0) return x ;

/* Итерации вычисления корня: */

r=x; /* r - очередное приближение */

do {

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

}

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

return r;

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

/* Определение функции */

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

{

if(z > 0) return z;

else return(-z);

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

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

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

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

Сторона a=2.0

Сторона b=3.0

Сторона c=4.0

Площадь треугольника: 2.904737

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

/* Скалярное произведение векторов */

#include

#define MAX_INDEX 5

void main( ) {

/* Прототип функции: */

float Scalar_Product(int, float[ ], float[ ]);

int n,i;

float x[MAX_INDEX],y[MAX_INDEX];

printf("\n Размерность векторов n= ");

scanf("%d",&n);

if(n < 1 || n >MAX_INDEX)

{

printf("\n Ошибка в данных!");

return; /* Аварийное завершение */

}

printf("Введите %d координ. x: ",n);

for (i=0; i
printf("Введите %d координ. y: ",n);

for (i=0; i
printf("\n Результат: %7.3f", Scalar_Product(n,x,y));

}

/* Определение функции scalar: */

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

/* Скалярное произведение n-мерных векторов */

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

/* a[ ],b[ ] - массивы координат векторов */

{ int i; /* Параметр цикла */

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

for (i=0,z=0.0; i < n; i++) z += a[i]*b[i];

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

В начале программы с помощью #define введена препроцессор- ная константа MAX_INDEX. Далее определены массивы, у которых пределы изменения индексов заданы на препроцессорном уровне. Именно эти пределы проверяются после ввода размерности век­торов (n). В теле функции main( ) приведен прототип функции Scalar_Product( ). Обратите внимание, что в прототипе отсутствуют имена параметров. Тот факт, что два параметра являются одномер­ными массивами, отображен спецификацией float[].

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

Размерность векторов n=2

Введите 2 координ. x: 1 3.1

Введите 2 координ. y: 1 2.1

Результат: 7.510

Другая попытка выполнить программу:

Размерность векторов n=0 Ошибка в данных!

Диаметр множества точек. Как еще один пример использования функций с массивами в качестве параметров рассмотрим программу определения диаметра множества точек в многомерном евклидовом пространстве. Напомним, что диаметром называется максимальное расстояние между точками множества, а расстояние в евклидовом пространстве между точками x = { xi }; y = { yi }, i = 1, ..., n, опре­деляется как

d(x, y) = л 1'-)

Введем ограничения на размерность пространства: N_MAX<=10 и количество точек K_MAX<=100. Текст программы может быть таким:

#include

#include /* для функции sqrt( ) */

/* Функция для вычисления расстояния между двумя точками */ float distance(float x[ ], float y[ ], int n)

{

int i;

float r,s=0.0;

for(i=0;i
{

r=y[i]-x[i]; s+=r*r;

} s=sqrt(s); return s;

}

#define K_MAX 100

#define N_MAX 10 void main( ) {

float dist, dist_max,d;

int i, j, i_max, m_max, n, k, m;

float a[K_MAX][N_MAX];

/* a[][] — Массив фиксированных размеров */

while(1) {

printf("\n Количество точек k=");

scanf("%d", &k);

printf("Размерность пространства n=");

scanf("%d", &n);

if(k>0 && k<=K_MAX && n>0 && n<=N_MAX) break; printf("ОШИБКА В Данных!");

}

for(i=0;i
{ /* Цикл ввода координат точек */ printf("Введите %d координ. "

"точки %d:\n",n,i+1);

for(j=0;j
/* Цикл ввода координат одной точки */ scanf("%f",&a[i][j]);

}

dist_max=0.0;

i_max=0;

m_max=0;

for(i=0;i
{ /* Цикл сравнения точек */ for(m=i+1;m
{

dist=distance(a[i],a[m],n); if(dist>dist_max)

{ dist_max=dist; i_max=i;

m_max=m; }

}

} /* Конец цикла по i */

printf("Результат:\пДиаметр=ОТ", dist_max);

printf("\n Номера точек : %d,%d", i_max+1,m_max+1);

}

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

Количество

точек к=4



Размерность

пространства n=3



Введите 3

координ. точки 1:




1 1

1



Введите 3

координ. точки 2:




-1 -1

-1



Введите 3

координ. точки 3:




2 2

2



Введите 3

координ. точки 4:




-2 -2

-2



Результат:

Диаметр = 6.928203

Номера точек: 3, 4

В программе особый интерес представляет обращение к функции distance( ), где в качестве аргументов используются индексирован­ные элементы a[i], a[m]. Каждый из них, по определению, есть одно­мерный массив из n элементов, что и учитывается в теле функции. Для задания размеров массива a[ ][ ] и предельных значений пере­менных k и n используются препроцессорные константы K_MAX и

N_MAX. Их нельзя определить как переменные, то есть ошибочной будет последовательность:

int K_MAX=100; N_MAX=10; float a[K_MAX][N_MAX];

При определении массивов их размеры можно задавать только с помощью константных выражений.

    1. Переключатели

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

switch( выражение )

    1. case константа1: операторы_1;

case константа2: операторы_2;

default: операторы;

}

В этом операторе используются три служебных слова: switch, case, default. Первое из них идентифицирует собственно оператор- переключатель. Служебное слово case с последующей константой является в некотором смысле меткой. Константы могут быть целы­ми или символьными и все должны быть различными (чтобы метки были различимы). Служебное слово default также обозначает от­дельную метку. При выполнении оператора (рис. 2.6а) вычисляется выражение, записанное после switch, и его значение последователь­но сравнивается с константами, которые помещены вслед за case. При первом же совпадении выполняются операторы, помеченные данной меткой. Если выполненные операторы не предусматривают какого-либо перехода (то есть среди них нет ни goto, ни return, ни exit( ), ни break), то далее выполняются операторы всех следующих вариантов, пока не появится оператор перехода или не закончится переключатель.

Операторы вслед за default выполняются, если значение выраже­ния в скобках после switch не совпало ни с одной константой после case. Метка default может в переключателе отсутствовать. В этом случае при несовпадении значения выражения с константами пере­ключатель не выполняет никаких действий. Операторы, помеченные меткой default, не обязательно находятся в конце (после других ва­риантов переключателя). Уточним, что default и «case константа»

не являются метками в обычном смысле. К ним, например, нельзя перейти с помощью оператора goto.

На рис. 2.6 приведены схемы переключателя (рис. 2.6а) и опера­тора альтернативного выбора или селектора (рис. 2.6 б), отсутствую­щего в языке Си.



а

Рис. 2.6. Переключатель (а) и альтернативный выбор (б): а - если выражение равно МК, то выполняются операторы Sk, Sk+1...S, S;

б - если выражение равно LK, то выполняются т,.о..,лько операторы Sk

На рис. 2.7 изображена схема альтернативного выбора, реализо­ванная при помощи переключателя и операторов перехода (go to, return, break), введенных в S1, S2, ..., Sn.

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

#include void main( )

{

int i;

while(1)

{

printf("\n Введите десятичную цифру: ");

scanf("%d",&i);



Рис. 2.7. Альтернативный выбор с использованием переключателя. В число операторов каждой группы Skдобавлен оператор выхода из переключателя


printf("\t\t"); switch( i )

f

1

case 1:

printf("%d -

один \n",i);

break;

case 2: break;

printf("%d -

два \n",i);

case 3:

printf("%d -

три \n",i);

break;

case 4: break;

printf("%d -

четыре \n",i)

case 5:

printf("%d -

пять \n",i);

break;

case 6: break;

printf("%d -

шесть \n",i);

case 7:

printf("%d -

семь \n",i);

break;

case 8: break;

printf("%d -

восемь \n",i)

case 9: printf("%d - девять \n",i);

break;

case 0: printf("%d - нуль \n",i);

break;

default: printf("%d - это не цифра! \n",i); return;

} /* Конец переключателя */

} /* Конец цикла */

}

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

Введите

десятичную

цифру: 3

3 - три



Введите

десятичную

цифру: 8

8 - восемь



Введите

десятичную

цифру: -7

-7 - это не цифра!



Программа прекращает выполнение, как только будет введен символ, отличный от цифры. Завершение программы обеспечивает оператор return; который в данном случае передает управление опе­рационной системе, так как выполняет выход из функции main( ).

Переключатель вместе с набором операторов break реализует в этой программе альтернативный выбор (см. рис. 2.7). Если удалить все операторы break, то работа переключателя в этой программе бу­дет соответствовать схеме рис. 2.6а.

Несколько «меток» case с разными значениями констант могут помечать один оператор внутри переключателя, что позволяет еще больше разнообразить схемы построения операторов switch.

Контрольные вопросы

  1. Можно ли написать завершенную программу без функции main()?

  2. Какое расширение должно иметь имя файла с текстом програм­мы на языке Си?

  3. Назовите обязательные этапы обработки, которые проходит ис­ходная программа, подготовленная на языке Си в виде тексто­вого файла.

  4. Объясните роль препроцессора при подготовке программы на языке Си.

  5. Какие требования существуют в отношении препроцессорных директив?

  6. Каким образом включают в программу прототипы библиотеч­ных функций?

  7. Какова структура простой программы на языке Си?

  8. Для чего служат библиотечные функции?

  9. Назовите состав тела функции.

  10. Для чего служат определения и описания?

  11. Как можно задать комментарий в тексте программы?

  12. Перечислите группы операторов языка Си.

  13. Какие операторы применяются для управления работой про­граммы?

  14. Чем отличается блок от составного оператора?

  15. В каких местах кода программы может быть поставлена метка?

  16. Как обозначается пустой оператор?

  17. Перечислите операторы циклов языка Си.

  18. Перечислите операторы ветвлений языка Си.

  19. В чем сходства и различия операторов break и continue?

  20. Что представляет собой инициализация объекта (например, мас­сива)?

  21. Значения каких типов могут возвращать функции?

  22. Как задается имя неглавной функции?

  23. Дайте определение списка параметров.

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

  25. Что такое прототип функции?

  26. Где размещают прототипы функций?

  27. Каким образом организуется мультиветвление в программе на языке Си?

  28. Чем отличаются действия переключателя и оператора альтерна­тивного выбора?

1   ...   6   7   8   9   10   11   12   13   ...   42


написать администратору сайта