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

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

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

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

выражение_условие. Любое из трех, любые два или все три вы­ ражения в операторе for могут отсутствовать, но разделяющие их символы должны присутствовать всегда. Если отсутствует выражение_условие, то считается, что оно истинно и нужны специальные средства для выхода из цикла.

Схемы организации циклов while, do и for даны на рис. 2.3. Проиллюстрируем особенности трех типов цикла на примере

вычисления приближенного значения

2

,3

СО

,п

 

 

 

для заданного значения х. Вычисления будем продолжать до тех пор, пока очередной член ряда остается больше заданной точно­ сти. Обозначим точность через eps, результат - Ь, очередной член ряда - г, номер члена ряда - i. Для получения i-ro члена ряда нужно (i-l)-fi член умножить на х и разделить на i, что по­ зволяет исключить операцию возведения в степень и явное вы­ числение факториала. Опустив определения переменных, опера­ торы ввода и проверки исходных данных, а также вывода ре­ зультатов, запишем три фрагмента программ.

/* Цикл с предусловием */ i = 2

Ь= 1.0;

г= х;

while( г > eps || г < -eps )

{

b=b+r;

r=r*x/i;

i++;

}

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

/* Цикл с постусловием */

i=l; Ь=0.О ;

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

93

а

в

Рис. 2.3. Схемы организации циклов while, do, for:

а - цикл с предусловием while; 6 - цикл с постусловием do; в - параметрический цикл for

94

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

г=1.О ;

do

{

. b=b+r; r=r*x/i; i++ ;

}

while( r >= eps || r <= -eps );

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

/* Параметрический цикл */ i=2;

Ь=1.О ; г=х ;

for( ; г > eps || г < -eps ; )

{

b=b+r;

r=r*x/i;

i=i+l;

}

Условие окончания параметрического цикла такое же, как и в цикле while.

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

for ( i=2, b=1.0, r=x; r>eps || r<-eps ; )

{

b=b+r;

r=r*x/i;

i=i+l;

}

В выражение З можно включать операцию изменения счет­ чика членов ряда:

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

95

for( i=2, b=l.О, r=x; r>eps || r<-eps; i++)

{

b=b+r;

r=r*x/i;

}

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

for(i=2, b=1.0, r=x; r>eps || r<-eps; b+=r, r*=x/i, i++);

В данном случае тело цикла - пустой оператор. Для сокра­ щения выражения_3 в нем использованы составные операции присваивания и операция ++.

Приближенное значение экспоненты. Приведем полный текст программы для приближенного вычисления экспоненты:

1

/*Приближенное Значение экспоненты*/

2

#include <stdio.h>

3

void main( )

 

4

{

/*i - счетчик членов ряда*/

5

int i;

6double eps,b=l.0,r,x;/*exp - абс. точность*/

7/*Ь-Значение экспоненты; r-очередной член*/

8/* х-вводимое Значение показателя экспоненты*/

9printf("\n Введите Значение х=") ;

10scanf("%lf",£х);

11do {

12printf("\n Введите точность eps=");

13scanf("%lf",£eps);

14}

15while( eps <= 0.0 );

16 for( i=2, r=x; r > eps || r < -eps; i++ )

17{

18b=b+r;

19r=r*x/i;

20}

21printf(” Результат: %f\n”,b);

22printf(" Погрешность: %f\n",r);

23printf(" Число членов ряда: %d\n",i);

24}

96

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

В программе переменная b инициализирована (строка 6), т.е. ей еще в процессе выделения памяти присваивается конкретное начальное значение (1.0). При вводе значения переменной eps предусмотрена защита от неверного задания точности (строки 11+15), для чего использован цикл do. В соответствии с прави­ лами его работы выполняются операторы из строк 12 и 13, а за­ тем проверяется введенное значение точности. Если значение eps недопустимо (eps<=0.0), то операторы тела цикла (строки 12, 13) выполняются снова. Цикл не закончится до тех пор, пока не будет введено допустимое значение точности.

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

Введите значение х=1 Введите точность eps=0.01 Результат: 2.708333 Погрешность: 0.008333 Число членов ряда: 6

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

Введите значение х=1 Введите точность eps=-0.3 Введите точность eps=0.0001 Результат: 2.718254 Погрешность: 0.000025 Число членов ряда: 9

В последнем варианте вначале введено неверное значение eps=-0.3 и ввод пришлось повторить. Отметим, что в качестве погрешности выводится последнее значение учтенного члена ряда, что не вполне корректно.

Оператор break. Как видно из предыдущего примера, при­ нятый способ проверки исходных данных с повторным запро­ сом значения точности eps не очень удобен, так как на экране отсутствует сообщение об ошибке. В примере, рассмотренном в предыдущем параграфе (сумма членов ряда Фибоначчи), на эк­ ран выводится конкретное указание о сделанной ошибке ("Ошибка! к должно быть > 2 !"), что упрощает ее исправление. Однако в этом случае в программе использованы метки и опера­

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

97

тор перехода, что считается некорректным с точки зрения структурного программирования.

Добиться такого же результата можно, не нарушая принципов структурного программирования и применяя оператор цикла, если использовать в его теле оператор прерывания break. Этот оператор (рис. 2.4) прекращает выполнение оператора цикла и передает управление следующему за ним (за циклом) оператору.

Необходимость в использовании оператора прерывания в те­ ле цикла возникает, когда условие продолжения итераций нуж­ но проверять не в начале цикла (как в циклах for и while) и не в конце тела цикла (как в цикле do), а в середине тела цикла. Наиболее естественна в этом случае такая структура тела цикла:

{

операторы

if (условие) break;

операторы

}

Рассмотрим пример с использованием оператора прерывания break в цикле ввода исходных данных.

Сумма отрезка степенного ряда. Введя значения перемен-

П

ных п и g, вычислить сумму с = ^ g ' , где п>0. Задачу решает 1=1

следующая программа:

1/* Сумма степенного ряда */

2#±nclude <stdio.h>

3void main( )

4{

5 double g,c,p; /* с - сумма, p - член ряда */

6int i,n;

7printf("\n Введите значение g=u);

8scanf("%lf",fig);

9while(1)

10{

11printf("\n Введите значение n=") ;

12scanf("%d",fin) ;

13if( n > 0 ) break;

7 ~ 3124

98

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

14printf("Ошибка! п должно быть >0! \п");

15}

16 for( с=0.0, р=1.0, i=l; i <= n; i++ )

17{

18p*=g;

19c+=p;

20}

21printf("\n Сумма c=%f",c);

22}

Для защиты от неверных исходных данных использован цикл (строки 9+15), в заголовке которого после while записано заве­ домо истинное выражение 1, т.е. цикл может быть прерван только за счет выполнения операторов его тела. В строке 13 оператор break выполняется в случае истинности условия "п>0". При этом цикл заканчивается, и следует переход к опера­ тору строки 16. Таким образом, сообщение об ошибке, выводи­ мое функцией printf() из строки 14, печатается для каждого неположительного значения п.

Вычисляя сумму ряда (строки 16+20), очередной его член получаем, умножая предыдущий на g (строка 18). Тем самым устранена необходимость явного возведения в степень. В вы­ ражении\_1 оператора for формируются начальные значения переменных, изменяемых при выполнении цикла. В строках 18, 19 использованы составные операции присваивания.

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

Введите значение д=8.8 Введите Значение п=-15 Ошибка! п должно быть >0! Введите значение п=15 Сумма с=165816655682177404000

Полученное значение суммы довольно трудно прочесть. Повидимому, в этой задаче следует применять печать результата в экспоненциальной форме, т.е. в printf() из строки 21 исполь­ зовать спецификацию преобразования %е. При этом получится такой результат:

Введите значение п=15 Сумма с=1:658167е+14.

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

99

Рис. 2.4. Схемы выполнения в циклах операторов break и continue:

а—цикл с предусловием while; 6 - цикл с постусловием do;

в- параметрический цикл for

100

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

Оператор continue. Еще одну возможность влиять на вы­ полнение операторов тела цикла обеспечивает оператор перехо­ да к следующей итерации цикла continue (см. рис. 2.4). Как указано в описании языка Си, "оператор continue противополо­ жен по действию оператору break". Он позволяет в любой точке тела цикла прервать текущую итерацию и перейти к проверке условий продолжения цикла, определенных в предложениях for или while. В соответствии с результатами проверки выполнение цикла либо заканчивается, либо начинается новая итерация. Оператор continue удобен, когда от итерации к итерации изме­ няется последовательность выполняемых операторов тела цикла, т.е. когда тело цикла содержит ветвления. Рассмотрим пример.

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

#±nclude <stdio.h>

/* Сумма положительных чисел */ void main( )

{

double s,x;/*x - очередное число, s - сумма*/ int k; /*k - количество положительных */ printf("\пВведите последовательность чисел"

" с 0 в конце:\п");

for( х=1.0, s=0.0, k=0; х != 0.0; )

{

scanf("%lf",£х);

if( х <= 0.0 ) continue; k++; s+=x;

}

printf("\n CyMMa=%f,

количество положительных=%d",s ,k);

}

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

Введите последовательность чисел с 0 в конце: б -3.0 14.0 -5 -4 10 0.0 Сумма=30.000000, количество положительных=3

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

101

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

2.4. М ассивы и влож ение операторов цикла

Массивы и переменные с индексами. Математическим по­ нятием, которое привело к появлению в языках программирова­ ния понятия "массив", являются матрица и ее частные случаи: вектор-столбец или вектор-строка. Элементы матриц в матема­ тике принято обозначать с использованием индексов. Сущест­ венно, что все элементы матриц либо вещественные, либо целые и т.п. Такая "однородность" элементов свойственна и массиву, определение которого описывает тип элементов, имя массива и его размерность, т.е. число индексов, необходимое для обозна­ чения конкретного элемента. Кроме того, в определении указы­ вается количество значений, принимаемых каждым индексом. Например, int а[10]; определяет массив из 10 элементов а[0], а[1], ..., а[9]. float Z[13][[6]; определяет двумерный массив, пер­ вый индекс которого принимает 13 значений от 0 до 12, второй индекс принимает 6 значений от 0 до 5. Таким образом, элемен­ ты двумерного массива Z можно перечислить так:

Z[0][0], Z[0][1], Z [0] [2],...,Z [12][4], Z [12][5]

В соответствии с синтаксисом Си в языке существуют только одномерные массивы, однако элементами одномерного массива, в свою очередь, могут быть массивы. Поэтому двумерный мас­ сив определяется как массив массивов. Таким образом, в при­ мере определен массив Z из 13 элементов-массивов, каждый из которых, в свою очередь, состоит из 6 элементов типа float. Об­ ратите внимание, что нумерация элементов любого массива все­ гда начинается с 0, т.е. индекс изменяется от 0 до N -1, где N - количество значений индекса.

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