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

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

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

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

w=24<ENTER>

x=22<ENTER>

y=77<ENTER>

z=9<ENTER>

min4(w,x,y,z)=9.000000

В программе кроме main() две внешние функции min2() и min4() для определения минимальных значений из двух и четырех параметров соответственно. Функция main() вызывает функцию min4(), в теле которой используется обращение к функции min2(). В этом обращении фактическим параметром служит другое обращение к той же функции min2() и т.д. Язык Си позволяет использовать такие многократные вложения вызовов функций.

ЗАДАЧА 08-03. Напишите функцию, соответствующую графику функциональной зависимости, приведенной на рис. 8.1. Функциональная зависимость параметрическая с параметром (а), принимающим положительные значения. В основной программе введите значения параметра настройки функциональной зависимости (a), предельные значения аргумента (xmin, xmax) и шаг

его изменения (x). Выведите значения функции для аргумента,

изменяющегося от xmin до xmax с шагом x.

Рис. 8.1. График зависимости для задачи 08-03

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

271

функции два параметра: один (parameter) параметризует функциональную зависимость, второй (х) является аргументом зависимости.

/* 08_03.c - параметрическая зависимость */ #include <stdio.h>

#define READD(VARIABLE) \

{ printf(#VARIABLE"="); scanf("%le",&VARIABLE); }

double function(double a, double x)

{

a = (a >= 0.0 ? a : -a); if (x <= -a) return x;

if (x <= 0.0 && x > -a) return - (2*a + x); if (x <= a && x > 0.0) return - (2*a - x); if (x > a) return -x;

}

int main()

{

double parameter,xMin,xMax,x,delta; READD(parameter);

READD(xMin);

READD(xMax);

READD(delta);

for (x=xMin; x <= xMax; x+=delta) printf

("\nf(%5.2f)=%6.2f",x,function(parameter,x)); return 0;

}

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

parameter=3<ENTER> xMin=-4<ENTER> xMax=2<ENTER> delta=1<ENTER>

f(-4.00)= -4.00 f(-3.00)= -3.00 f(-2.00)= -4.00 f(-1.00)= -5.00 f( 0.00)= -6.00

272

f( 1.00)= -5.00 f( 2.00)= -4.00

В приведенном решении (в программе 08_03.с) значение параметра настройки функциональной зависимости (parameter) вводится один раз и более не изменяется до конца программы. В цикле от xMin до xMax изменяется значение аргумента x и вычисляется (печатается) значение функциональной зависимости.

ЗАДАНИЕ. В решении предыдущей задачи измените способ обмена данными между основной программой и функцией, реализующей вычисление значений параметрической функциональной зависимости. Функцию определите с одним параметром – аргументом x, а значение параметра настройки функциональной зависимости (а) передавайте в тело функции с помощью глобальной переменной (factor).

/* 08_03_1.c - глобальная переменная для обмена между функциями */

#include <stdio.h> #define READD(VARIABLE) \

{ printf(#VARIABLE"="); scanf("%le",&VARIABLE); } double factor;

double function(double x)

{

factor = (factor >= 0.0 ? factor : -factor); if (x <= -factor) return x;

if (x <= 0.0 && x > -factor) return -(2*factor+x); if (x <= factor && x > 0.0) return -(2*factor-x); if (x > factor) return -x;

}

int main()

{

double xMin,xMax,x,delta; READD(factor); READD(xMin);

READD(xMax);

READD(delta);

for (x=xMin; x <= xMax; x+=delta) printf("\nf(%5.2f)=%6.2f",x,function(x));

return 0;

}

273

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

factor=4<ENTER> xMin=-4<ENTER> xMax=6<ENTER> delta=2<ENTER>

f(-4.00)= -4.00 f(-2.00)= -6.00 f( 0.00)= -8.00 f( 2.00)= -6.00 f( 4.00)= -4.00 f( 6.00)= -6.00

В данной программе у функции function() только один параметр. С помощью внешней (глобальной) переменной (double factor) она параметризует соответствующую функциональную зависимость.

ЗАДАНИЕ. Определите препроцессорный идентификатор FACTOR и поставьте ему в соответствие желаемое значение параметра настройки функциональной зависимости. (Уберите из функции main() ввод значения переменной factor и добавьте вывод значения, поставленного в соответствие идентификатору

FACTOR.)

Следующая программа решает задачу указанным образом:

/* 08_03_2.c - параметр зависимости задан препроцессорным идентификатором */

#include <stdio.h> #define READD(VARIABLE) \

{ printf(#VARIABLE"="); scanf("%le",&VARIABLE); } #define FACTOR 4.0

double function(double x)

{

double factor;

factor = (FACTOR >= 0.0 ? FACTOR : -FACTOR); if (x <= -factor) return x;

if (x <= 0.0 && x > -factor) return -(2*factor+x); if (x <= factor && x > 0.0) return -(2*factor-x); if (x > factor) return -x;

274

}

int main()

{

double xMin,xMax,x,delta; READD(xMin);

READD(xMax);

READD(delta);

printf("\nFACTOR=%f",FACTOR);

for (x=xMin; x <= xMax; x+=delta) printf("\nf(%5.2f)=%6.2f",x,function(x));

return 0;

}

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

xMin=-3<ENTER> xMax=7<ENTER> delta=2<ENTER>

FACTOR=4.000000 f(-3.00)= -5.00 f(-1.00)= -7.00 f( 1.00)= -7.00 f( 3.00)= -5.00 f( 5.00)= -5.00 f( 7.00)= -7.00

Сравним особенности решения одной задачи 08-03 тремя разными способами. Наибольшую гибкость обеспечивает функция

double functuon (double parameter, double x);

из программы 08_03.с. Для ее использования достаточно знать ее прототип, который полностью определяет, какие исходные данные нужны для правильного использования функции.

Функция из программы 08_03_1.с с прототипом double function(double x); имеет практически те же возможности, но при ее использовании необходимо знать, что ей с помощью глобальной переменной double factor передается информация, которой "не видно" из рассмотрения прототипа.

Функция из программы 08_03_2.с с тем же прототипом, что и функция из предыдущей программы, имеет еще меньшую гибкость.

275

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

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

ЗАДАЧА 08-04. Выведите таблицу значений показательных (ex, e-x) и тригонометрических (sin x, cos x) функций, вводя пре-

дельные (xmin, xmax) значения аргумента x и шаг его изменения. Для печати одной строки таблицы напишите функцию с парамет-

ром, задающим значение аргумента (double x), и параметром, определяющим номер строки (int n).

// 08_04.c - таблица значений математических функций

#include <stdio.h> #include <math.h>

#define READD(VARIABLE) \

{

printf(#VARIABLE"="); scanf("%le",&VARIABLE); }

void

linePrint(int n, double x)

{

(n <= 0)

 

if

 

"\n|

{ printf(

| exp(x)|exp(-x)| sin(x)| cos(x)|");

# |

x

 

printf(

 

 

"\n|====+=======+=======+=======+=======+=======|");

}

else printf

276

("\n| %2d | %5.2f | %5.2f | %5.2f |%5.2f |%5.2f |", n,x,exp(x),exp(-x),sin(x),cos(x));

}

int main()

{

int line=0;

double xMin,xMax,x,delta; READD(xMin);

READD(xMax);

READD(delta); linePrint(0, x);

for (x=xMin; x <= xMax; x+=delta) linePrint(++line, x);

return 0;

}

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

xMin=1<ENTER>

xMax=2<ENTER>

delta=0.3<ENTER>

| # | x | exp(x)|exp(-x)| sin(x)| cos(x)| |====+=======+=======+=======+=======+=======|

|

1

|

1.00

|

2.72

|

0.37

|

0.84

|

0.54

|

|

2

|

1.30

|

3.67

|

0.27

|

0.96

|

0.27

|

|

3

|

1.60

|

4.95

|

0.20

|

1.00

| -0.03

|

|

4

|

1.90

|

6.69

|

0.15

|

0.95

| -0.32

|

Впрограмме функция linePrint(int n, double x) при неположительном значении номера строки n напечатает "шапку" таблицы. При положительном значении параметра n печатается номер строки, значение аргумента x и соответствующие ему значения функций.

Вфункции main() с помощью макроса READD() вводятся значе-

ния переменных xMin, xMax, delta. Обращение к функции с нулевым значением первого параметра обеспечивает печать заголовка таблицы. Затем в цикле функция linePrint() вызывается для разных значений номера строки (переменная line) и аргумента (переменная x – параметр цикла).

277

Ограничения. При выбранных спецификациях преобразования (%5.2f) таблица аккуратно выводится, когда значения ex и e-x не превосходят 99.99.

ЭКСПЕРИМЕНТ. Замените в теле функции linePrint() полный условный оператор (if… else…) сокращенным условным с опера-

тором return.

Возможный вариант функции (из программы 08_04_1.с):

void

linePrint(int n, double x)

{

(n

<= 0)

 

if

 

"\n|

{ printf(

| exp(x)|exp(-x)| sin(x)| cos(x)|");

#

|

x

printf(

"\n|====+=======+=======+=======+=======+=======|");

return;

}

printf(

"\n| %2d | %5.2f | %5.2f | %5.2f | %5.2f | %5.2f |", n,x,exp(x),exp(-x),sin(x),cos(x));

}

8.2. Адреса объектов и функции

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

ЗАДАЧА 08-05. Напечатайте функцию, возводящую (в теле функции) в квадрат значение своего параметра. В теле функции результат возведения в квадрат вначале присвойте параметру, затем выведите на печать адрес параметра и его значение. В основной программе при обращении к функции в качестве аргумента используйте переменную. Напечатайте адрес и значение этой переменной до и после обращения к функции.

278

Задачу решает программа:

/* 08_05.c - адреса и значения параметров и аргументов функции */

#include <stdio.h> long product(int p)

{

p = p*p; printf

("\nParameter: address = %p, value = %d", &p, p); return p;

}

int main()

{

int z=10; long result; printf

("\nArgument: address = %p, value = %d", &z, z); result = product(z);

printf

("\nArgument: address = %p, value = %d", &z, z); printf("\nresult=%ld",result);

return 0;

}

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

Argument: address = 372fd0, value = 10 Parameter: address = 372fc8, value = 100 Argument: address = 372fd0, value = 10 result=100

Как подтверждают результаты, адрес аргумента не совпадает с адресом параметра. Это разные объекты, им выделены разные участки памяти. Поэтому совершенно естественно, что значение аргумента (переменная z) остается без изменения, хотя параметр (переменная р в теле функции) возведен в квадрат.

ЭКСПЕРИМЕНТ. Выполните в основной программе несколько вызовов функции product() и сравните значения напечатанных адресов ее параметра.

279

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

ЗАДАНИЕ. Дополните программу "промежуточной" функцией с параметром того же типа, что и параметр функции product(). В теле промежуточной функции вызывается функция product() и печатаются адрес и значение параметра.

Возможный вариант решения:

/* 08_05_1.c – аргументы при вложении вызовов функций */

#include <stdio.h> long product(int p)

{

p = p*p;

printf("\nParameter of product():"

" address=%p, value=%d",&p,p); return p;

}

long ggg(int zzz)

{

printf("\nParameter of ggg():"

" address=%p, value=%d",&zzz,zzz); return product(zzz);

}

int main()

{

int z=10; long result;

printf ("\nArgument: address=%p, value=%d",&z,z); result = product(z);

printf ("\nArgument: address=%p, value=%d",&z,z); result=ggg(result); printf("\nresult=%ld",result);

return 0;

}

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

280