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

Алгоритмы и структуры данных. Учебное пособие по направлению подготовки 09.03.02 «Информационные системы и технологии»

.pdf
Скачиваний:
0
Добавлен:
06.09.2026
Размер:
1 Мб
Скачать
Перед каждым вводом помещены вызовы функции printf( ), выво­дящей на экран запрос-подсказку, вслед за которой на экране отображается набираемое на клавиатуре вводимое значение. Функция scanf() считывает только это значение, как только будет нажата клавиша "Ввод" (Enter), что воспринимается как признак конца строки ввода. Поэтому очередной вызов функции printf( ) выводит данные на следующую строку. Обратите внимание на спецификации преобразования %lf Если бы переменные h и r имели тип float, то в форматных строках функций scanf() нужно было бы применять спецификации %f или %е. Текст на экране при выполнении программы может быть таким:
радиус цилиндра r= 2.0
высота цилиндра h= 4.0
объем цилиндра: 50.2654
Здесь пользователь ввел 2.0 для r и 4.0 для h. Другой вариант:
радиус цилиндра r= 4.0
высота цилиндра h= 2.0
объем цилиндра: 100.5309
Еще раз обратите внимание на использование в функции scanf() не имен переменных, а их адресов &r, &h.
Сумма членов ряда Фибоначчи. Ряд Фибоначчи определен, если
известны первые два его члена f1, f2, так как очередной член fi =f
i-1
+ f
i-2
для
r>2. Необходимо вычислить сумму заданного количества (k) первых чле-
нов ряда Фибоначчи, если известны первые два: р = F1 и r = F2. Следующая программа решает эту задачу:
/*Вычисление суммы членов ряда Фибоначчи*/
#include <stdio.h>
void main( )
{
int k,i;/*к-число членов; i-номер члена */
float s,p,r,f; /* s - искомая сумма */
/*Члены: р -первый; r - второй; f
- i-й*/
Ml: printf("\n Введите число членов ряда k=") ;
scanf ("%d",&k) ;
if( k > 2 ) goto M2;
printf("\n Ошибка! k должно быть > 2 !");
goto Ml;
M2: printf("\n Первый член ряда р=") ;
scanf("%f",&p);
printf("\n Второй член ряда r=");
scanf("%fn,&r);
i = 3;
s = p + r;
101
M: f = p + r; s = s + f; p = r; r = f ; i = i + 1; if ( i <= k ) goto M; printf("\n Сумма членов ряда: %10.3f", s); }
Обратите внимание на строки, где выполняется проверка введенного значения k. Программа может правильно работать только при к>2, поэтому только в этом случае выполняется переход к метке М2. В противном случае печатается сообщение об ошибке, и после перехода к метке Ml запрашивается новое значение k.
Особенность и недостаток программы состоят в том, что она никогда не закончит вычислений, если не ввести допустимого значения к>2.
10.3. Операторы цикла
Три формы операторов цикла. В языке Си равноправно использу-
ются три разных оператора цикла, обозначаемых соответственно служеб­ными словами while, for, do.
Цикл while (цикл с предусловием) имеет вид:
while (выражение_условие)
тело_цикла
В качестве выражения_условия чаще всего используется отношение или логическое выражение. Если оно истинно, т.е. не равно 0, то тело цик­ла выполняется до тех пор, пока
Проверка истинности выражения осуществляется до каждого выпол­нения тела цикла. Таким образом, для заведомо ложного выраж-
ния_условия тело цикла не выполнится ни разу. Выражение _условие
может быть и арифметическим выражением. В этом случае цикл выпол­няется, пока значение выражения_условия не равно 0.
Цикл do (цикл с постусловием) имеет вид:
do
тел_ цикла
while (выражение_условие)\
Выражени_условие логическое или арифметическое, как и в цикле
while. В цикле do тело цикла всегда выполняется по крайней мере один раз. После каждого выполнения тела цикла проверяется истинность
выражения_условия (на равенство 0), и если оно ложно (т.е. равно 0), то
цикл заканчивается. В противном случае тело цикла выполняется вновь.
Цикл for (называемый параметрическим) имеет вид:
выражение_условие не станет ложным.
102
for (выражение_1; выражение_условие; выражение_3)
x
тело_цикла
Первое и третье выражения в операторе for могут состоять из
нескольких выражений, разделенных запятыми.
Выражение_1 определяет
действия, выполняемые до начала цикла, т.е. задает начальные условия для цикла; чаще всего это выражение присваивания.
Выражение_условие –
обычно логическое или арифметическое. Оно определяет условия оконча­ния или продолжения цикла. Если оно истинно (т.е. не равно 0), то выпол­няется тело цикла, а затем вычисляется
выражение _3. Выражение_3
обычно задает необходимые для следующей итерации изменения парамет­ров или любых переменных тела цикла. После выполнения
выражения_3
вычисляется истинность выражения_условия, и все повторяется... Таким образом,
выражение_1 вычисляется только один раз, а выражение_условие
и выражение_3 вычисляются после каждого выполнения тела цикла. Цикл продолжается до тех пор, пока не станет ложным
выражение_условие.
Любое из трех, любые два или все три выражения в операторе for могут отсутствовать, но разделяющие их символы ";" должны присутствовать всегда. Если отсутствует
выражение_усповие, то считается, что оно
истинно и нужны специальные средства для выхода из цикла.
Схемы организации циклов while, do и for даны на рис. 51. Проиллюстрируем особенности трех типов цикла на примере вычис-
ления приближенного значения
23
x
e
1...
   
xx x
1! 2! 3! !
n
n
0
n
для заданного значения х. Вычисления будем продолжать до тех пор, пока очередной член ряда остается больше заданной точности. Обозначим точность через eps, результат – b, очередной член ряда – г, номер члена ряда – i. Для получения i-гo члена ряда нужно (i-l)-й член умножить на х и разделить на i, что позволяет исключить операцию возведения в степень и явное вычисление факториала. Опустив определения переменных, опера­торы ввода и проверки исходных данных, а также вывода результатов, запишем три фрагмента программ.
/* Цикл с предусловием */ i = 2; b = 1.0; r = х; while( r > eps || r < -eps ) b=b+r; r=r*x/i; i++; }
103
Рис.51. Схемы организации циклов while, do, for:
а – цикл с предусловием while; б – цикл с постусловием do;
в – параметрический цикл for
Так как проверка точности проводится до выполнения тела цикла, то для окончания цикла абсолютное значение очередного члена должно быть меньше или равно заданной точности.
/* Цикл с постусловием */
i=1;
b=0.0;
r=1.0;
do {
b-b+r;
104
r=r*x/i; i++; } while( r >= eps || r <= -eps ) ;
Так как проверка точности осуществляется после выполнения тела цикла, то условие окончания цикла – абсолютное значение очередного чле­на строго меньше заданной точности. Соответствующие изменения вне­сены и в операторы, выполняемые до цикла.
/* Параметрический цикл */
i=2;d=1.0;r=х;
for( ; r > eps | | r < -eps ; )
{
b=b+r;
r=r*x/i;
i=i+1;
Условие окончания параметрического цикла такое же, как и в цикле while.
Все три цикла записаны по возможности одинаково, чтобы подчерк­нуть сходство циклов. Однако в данном примере цикл for имеет суще­ственные преимущества. В заголовок цикла (в качестве выражения_1) можно ввести инициализацию всех переменных:
for ( i=2, b=l.0, r=х; r>eps || r<-eps ; )
{
b=b+r;
r=r*x/i;
i=i+1;
}
В выражение_З можно включать операцию изменения счетчика членов ряда:
for( i=2, b=1.0, 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 в нем использованы составные операции присваивания и операция ++.
105
Оператор break. Этот оператор прекращает выполнение оператора
цикла и передает управление следующему за ним (за циклом) оператору.
Необходимость в использовании оператора прерывания в теле цикла возникает, когда условие продолжения итераций нужно проверять не в начале цикла (как в циклах for и while) и не в конце тела цикла (как в цикле do), а в середине тела цикла. Наиболее естественна в этом случае такая структура тела цикла:
{
операторы
if (условие) break;
операторы
}
Пример:
while(1)
{
printf ("\n Введите значение n=”);
scanf("%d",n);
if( n > 0 ) break;
printf ("\n ошибка! n должно быть >0!\n”);
}
Оператор continue. Еще одну возможность влиять на выполнение
операторов тела цикла обеспечивает оператор перехода к следующей итерации цикла continue. Оператор continue противоположен по действию оператору break. Он позволяет в любой точке тела цикла прервать теку­щую итерацию и перейти к проверке условий продолжения цикла, опреде­ленных в предложениях for или while. В соответствии с результатами проверки выполнение цикла либо заканчивается, либо начинается новая итерация. Оператор continue удобен, когда от итерации к итерации изме­няется последовательность выполняемых операторов тела цикла, т.е. когда тело цикла содержит ветвления. Рассмотрим пример.
Суммирование положительных чисел. Вводя последовательность чисел, заканчивающуюся нулем, получить сумму и количество только положительных из них. Следующая программа решает эту задачу:
#include <stdio.h>
/* Сумма положительных чисел */
void main( )
{
double s,x;/*x - очередное число, s - сумма*/
int k; /*k - количество положительных */
printf("\nВведите последовательность чисел"
" с 0 в конце:\n");
for( х=1.0, s=0.0, k=0; x !> 0.0; )
106
{ scanf("%lf",x); if( х <= 0.0 ) continue; k++;s+=x; } printf("\n Cyммa=%f, количество положительных=%<d" rs,k) ; }
Результат выполнения программы: Введите последовательность чисел с 0 в конце: б -3.0 14.0 -5 -4 10 0.0 Сумма=30.000000, количество положительных=3
Недостаток приведенной программы состоит в том, что нет защиты от неверно введенных данных. Например, не предусмотрены действия, когда в последовательности отсутствует нулевой элемент. Обратите внимание на объединение двух строк в функции printf().
Проверка на четность:
вариант 1:
"a&1" дает истину, когда число нечетное.
Пример проверки четности:
if (a&1) printf("Число нечетно");
else printf("Число четно");
вариант 2
#include <stdio.h>
void main()
{int i, s,a[100];
s=0;
printf("\nvvedi massiv\n");
for(i=1;i<=10;i++)
{
scanf("%d",&a[i]);
printf("\na=%d",a[i]);
if (a[i]%2==0) /*для четных*/
/* if (a[i]%2) для нечетных*/
/*if (a[i]%2!=0) для нечетных*/
{s+=a[i]; printf("\ns=%d",s);}
}
printf("\ns=%d",s);
}
107
10.4. Массивы и вложение операторов цикла
Массивы и переменные с индексами. Математическим понятием, кото­рое привело к появлению в языках программирования понятия "массив", являются матрица и ее частные случаи: вектор-столбец или вектор-строка. Элементы матриц в математике принято обозначать с использованием индексов. Существенно, что все элементы матриц либо вещественные, либо целые и т.п. Такая "однородность" элементов свойственна и массиву, определение которого описывает тип элементов, имя массива и его размер­ность, т.е. число индексов, необходимое для обозначения конкретного элемента. Кроме того, в определении указывается количество, принима­емых каждым индексом. Например, int a[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 до N-1, где N – количество значений индекса.
Ограничений на размерность массивов, т.е. на число индексов у его элементов, в языке Си теоретически нет. Стандарт языка Си требует, чтобы транслятор мог обрабатывать определения массивов с размерностью до 31. Однако чаще всего используются одномерные и двумерные массивы. Рас­смотрим на простых вычислительных задачах некоторые приемы работы с массивами.
Вычисление среднего. Введя значение n из диапазона (0<n<=100) и зна­чения n первых элементов массива х[0], х[1],...,х[n-1], вычислить среднее и оценку дисперсии значений введенных элементов массива. Задачу решает следующая программа:
/* Вычисление среднего */
#include <stdio.h>
void main ( )
{ /*n - количество элементов */
int i,n;/*b-среднее */
float b,x[100];
while (1)
{
любого массива всегда начинается с 0, т.е.
108
printf("\n Введите Значение n=");
s

scanf("%d", n); if ( n > 0 && n <= 100 ) break; printf("\n Ошибка! Необходимо 0<n<101 ") ; } /* Конец цикла ввода значения n */ printf("\n Введите Значения элементов:\n"); for( b=0.0,i=0; i<n; i++) { printf("x[%d] =", i); scanf("%f", &x[i]); b+=x[i]; /* Вычисление суммы элементов */ } b/=n;/* Вычисление среднего */ printf("\n Среднее =%f, b) ; }
В программе определен массив х со 100 элементами, хотя в каждом конкретном случае используются только первые n из них. Ввод значения п сопровождается проверкой допустимости вводимого значения. В качестве условия после while записано заведомо истинное выражение 1, поэтому выход из цикла (оператор break) возможен только при вводе правильного значения n, удовлетворяющего неравенству 0<n<101. Следующий цикл обеспечивает ввод n элементов массива и получение их суммы (b).
Вложенные циклы. В теле цикла разрешены любые исполнимые
операторы, в том числе и циклы, т.е. можно конструировать вложенные циклы.
В качестве примера рассмотрим фрагмент программы для получения суммы такого вида:
5
10 5


11
ji
aa
i
1
ji ji
.
Введем следующие обозначения: а – двумерный массив, содержащий значения элементов матрицы; р – произведение элементов строки матри­цы; с – сумма их значений; s – искомая сумма (результат). Опустив опреде­ления переменных и операторы ввода-вывода, запишем текст на языке Си:
double a[10][5];
for( s=0.0,j=0; j<10; j++)
{
for( p=1.0,c=0.0,i=0; i<5; i++)
{
p*=a[j][i];
c+=a[j][i];
}
109
s+=c+p;
j
s
}
При работе с вложенными циклами следует обратить внимание на правила выполнения операторов break и continue. Каждый из них действует только в том операторе, в теле которого он непосредственно использован. Оператор break прекращает выполнение ближайшего внешнего для него оператора цикла. Оператор continue передает управление на ближайшую внешнюю проверку условия продолжения цикла.
Для иллюстрации рассмотрим фрагмент другой программы для вычисления суммы произведений элементов строк той же матрицы:
5
10
j
a
1
i
i
1
double a[10][5];
for (j=0,s=0.0; j<10; j++)
{
for (i=0,p=1.0; i<5; i++)
{
if (a[j][i] ==0.0) break;
p*=a[j][i];
}
if (i <5) continue;
s+=p;
}
Внутренний цикл по i прерывается, если обнаруживается нулевой элемент матрицы. В этом случае произведение элементов столбца заведомо равно нулю, и его не нужно вычислять. Во внешнем цикле проверяется значение i. Если i<5, т.е. элемент a[j][i] оказался нулевым, то оператор continue передает управление на ближайший оператор цикла, и, таким образом, не происходит увеличение s на величину "недосчитанного" значения р. Если внутренний цикл завершен естественно, то i равно 5, и оператор continue не может выполняться.
Упорядочение в одномерных массивах. Для демонстрации некото-
рых особенностей вложения циклов и работы с массивами рассмотрим простейшие алгоритмы сортировки. Необходимо, введя значение перемен­ной 1<n<=100 и значения n первых элементов массива а[0],а[1],...,а[n-1], упорядочить эти первые элементы массива по возрастанию их значений. Текст первого варианта программы:
/* Упорядочение элементов массива */
#include <stdio.h>
main( )
{
int n,i,j;
110
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]