
книги / Программирование на языке Си
..pdf92 Программирование на языке Си
выражение_условие. Любое из трех, любые два или все три вы ражения в операторе 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 - количество значений индекса.