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

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

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

82

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

printf("\n Корни: xl=%e,

х2=%е", х1, х2);

>

else

pr±ntf("\n Действительные корни отсутствуют.");

Во фрагменте предполагается, что переменные d, b, a, xl, х2 - вещественные (типа float либо double). До приведенных опе­ раторов переменные а, Ь, с получили конкретные значения, для которых выполняются вычисления. В условном операторе после if находится составной оператор, после else - только один опе­ ратор - вызов функции printf(). При вычислении корней ис­ пользуется библиотечная функция sq rt() из стандартной библиотеки компилятора. Ее прототип находится в заголовоч­ ном файле math.h (см. Приложение 3).

Метки и пустой оператор. Метка - это идентификатор, по­ мещаемый слева от оператора и отделенный от него двоеточием

Например,

СОН: Х+=-8;

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

МЕТКА:;

Оператор перехода. Оператор безусловного перехода имеет следующий вид:

goto идентификатор;

где идентификатор - одна из меток программы. Например:

goto СОН; или goto МЕТКА;

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

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

83

Программа оценка машинного нуля. В вычислительных задачах при программировании итерационных алгоритмов, за­ вершающихся при достижении заданной точности, часто нужна оценка "машинного нуля", т.е. числового значения, меньше ко­ торого невозможно задавать точность данного алгоритма. Абсо­ лютное значение "машинного нуля" зависит от разрядной сетки применяемой ЭВМ, от принятой в конкретном трансляторе точ­ ности представления вещественных чисел и от значений, ис­ пользуемых для оценки точности. Следующая программа оценивает абсолютное значение "машинного нуля" относитель­ но близких (по модулю) к единице переменных типа float:

1/* Оценка "машинного нуля" */

2#include <stdio.h>

3void ma±n( )

4

{ int k;

/*k

-

счетчик итераций*/

5

float e,el;/*el

- вспомогательная переменная*/

6

е=1.0;

/*е

-

формируемый результат*/

7

к=0;

 

 

 

8М:е=е/2.0;

9е1=е+1.0;

10к=к+1 ;

11if (el>1.0) goto М;

12printf ("\п число делений на 2: %6d\n", к);

13printf ("машинный нуль: %е\п", е);

14}

Встроках программы слева помещены порядковые номера, которых нет в исходном тексте. Номера добавлены только для удобства ссылок на операторы. Строка 1 - комментарий с на­ званием программы. Комментарии в строках 4, 5, 6 поясняют назначение переменных. Объяснить работу программы проще всего с помощью трассировочной таблицы (табл. 2.1).

Во втором столбце таблицы указаны номера строк с испол­ няемыми операторами. Значения переменных даны после вы­ полнения соответствующего оператора. Только что измененное значение переменной в таблице выделено. После подготови­ тельных присваиваний (строки 6, 7) циклически выполняются

6*

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

операторы 8-11 до тех пор, пока истинно отношение е1>1.0, проверяемое в условном операторе. При каждой итерации зна­ чение переменной е уменьшается вдвое, и, наконец, прибавле­ ние (в строке 9) к 1.0 значения е не изменит результата, т.е. el станет равно 1.0.

При использовании компилятора Turbo С получен следую­ щий результат:

Число делений на 2: 24

Машинный нуль: 5.9б04б4е-08

При использовании в строке 5 для определения переменных е, el типа double, т.е. при использовании двойной точности, по­ лучен иной результат:

Число делений на 2: 53

Машинный нуль: 1.110223е-1б

Оба результата близки к значениям, приведенным в Прило­ жении 2, для предельных констант FLTEPSILO N и DBL_EPSILON.

 

 

 

 

 

Таблица 2.1

 

 

Трассировочнаятаблица

 

Шаг

 

Номер

 

Значения переменных

выполнения

строки

е

к

el

г

 

6

м

-

-

2

 

7

1.0

0

-

3

 

8

0

-

4

9

0.5

0

L5

5

 

10

0.5

1

1.5

б

 

11

0.5

1

1.5

7

 

8

0.2S

1

1.5

8

 

9

0.25

1

1.25

9

 

10

0.25

1

1.25

10

 

11

0.25

2

1.25

11

 

8

0.12S

2

1.25

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

85

Ввод данных. Для ввода данных с клавиатуры ЭВМ в про­ грамме будем использовать функцию (описана в заголовочном файле stdio.h):

scanf {форматная строка, списокаргументов);

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

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

% * ширина поля модификатор спецификатор

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

d - для целых десятичных чисел (тип int);

и - для целых десятичных чисел без знака (тип unsigned int);^ f - для вещественных чисел (тип float);

е - для вещественных чисел (тип float).

Ширина_поля - целое положительное число, позволяющее определить, какое количество байтов (символов) из входного потока соответствует вводимому значению. Этим элементом мы сейчас не будем пользоваться.

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

86

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

В качестве модификаторов используются символы:

 

h - для ввода значений типа short int (hd);

 

1

- для ввода значений типа long int (Id) или double (If, le);

L - для ввода значений типа long double (Lf, Le).

 

В ближайших программах нам не потребуются ни

ни мо­

дификаторы. Информацию о них приводим только для полноты сведений о спецификациях преобразования данных при вводе.

В отличие от функции printf() аргументами для функции scanf( ) могут быть только адреса объектов программы, в част­ ном случае - адреса ее переменных. Не расшифровывая понятие адреса (адресам и указателям будет посвящена гл. 4), напомним, что в языке Си имеется специальная унарная операция & полу­ чения адреса объекта:

& имя объекта

Выражение для получения адреса переменной будет таким:

& имя переменной

Итак, для обозначения адреса перед именем переменной за­ писывают символ &. Если name - имя переменной, то &name - ее адрес.

Например, для ввода с клавиатуры значений переменных n, z, х можно записать оператор:

scan f ("%d%f%f" , &n,&z, &x) ;

В данном примере спецификации преобразования в формат­ ной строке не содержат сведений о размерах полей и точностях вводимых значений. Это разрешено и очень удобно при вводе данных, диапазон значений которых определен не строго. Если переменная п описана как целая, z и х - как вещественные типа float, то после чтения с клавиатуры последовательности симво­ лов 18 18 -0.431 переменная п получит значение 18, z - значение 18.0, х-значение -0.431.

При чтении входных данных функция scanf() (в которой спецификации не содержат сведений о длинах вводимых значе­ ний) воспринимает в качестве разделителей полей данных "обобщенные пробельные символы" - собственно пробелы,

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

87

символы табуляции, символы новых строк. Изображения этих символов на экране отсутствуют, но у них имеются коды, кото­ рые "умеет" распознавать функция scanf(). При наборе входной информации на клавиатуре функция scanf() начинает ввод дан­ ных после нажатия клавиши <Enter> (переход на новую строку). До этого набираемые на клавиатуре символы помещаются в специально выделенную операционной системой область памя­ ти - в буфер клавиатуры и одновременно отображаются на эк­ ране в виде строки ввода. До нажатия клавиши <Enter> разрешено редактировать (исправлять) данные, подготовленные в строке ввода.

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

Вычисление объема цилиндра. В предыдущих задачах не требовалось вводить исходные данные. После выполнения про­ граммы на экране появлялся результат. Более общий случай - программа, требующая ввода исходных данных:

1/*Вычисление объема прямого цилиндра*/

2#include <stdio.h>

3void main( )

4{

5double h, r, v;

6const float PI = 3.14159;

7/*h - высота цилиндра, г -радиус цилиндра*/

8 /*v - объем цилиндра, PI - число "пи" */ 9 printf("\n Радиус цилиндра г= ");

10scanf("%lf", &r);

11printf("Высота цилиндра h= "); .

12scanf("%lf'V &h);

13v = h * PI * r * r;

14printf("Объем цилиндра: %10.4f" ,v) ;

15}

Втексте программы несколько особенностей. Определена константа PI, т.е. со значением 3.14159 связано имя PI, которое до конца выполнения программы будет именовать только это

значение.

88

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

Перед каждым вводом помещены (строки 9, 11) вызовы функции printf( ), выводящей на экран запрос-подсказку, вслед за которой на экране отображается набираемое на клавиатуре вводимое значение. Функция scanf() считывает только это зна­ чение, как только будет нажата клавиша "Ввод" (Enter), что воспринимается как признак конца строки ввода. Поэтому оче­ редной вызов функции printf( ) выводит данные на следующую строку. Обратите внимание на спецификации преобразования %lf. Если бы переменные h и г имели тип float, то в форматных строках функций scanf() нужно было бы применять специфи­ кации % f или %е. Текст на экране при выполнении программы может быть таким:

радиус цилиндра г= 2.0 высота цилиндра h= 4.0 объем цилиндра: 50.2654

Здесь пользователь ввел 2.0 для г и 4.0 для h. Другой вариант:

радиус цилиндра г= 4.0 высота цилиндра h= 2.0 об'ъем цилиндра: 100.5309

Еще раз обратите внимание на использование в функции scanf() не имен переменных, а их адресов &r, &h.

Сумма членов ряда Фибоначчи. Ряд Фибоначчи определен, если известны первые два его члена f), f2, так как очередной член fj =f j i + fj.2 для i>2. Необходимо вычислить сумму задан­ ного количества (к) первых членов ряда Фибоначчи, если из­ вестны первые два: р = F| и г = F2 . Следующая программа решает эту задачу:

1/♦Вычисление суммы членов ряда Фибоначчи*/

2#include <stdio.h>

3void main( )

4{

5

 

int It,i ;/*к-число

членов; i-номер члена */

6

 

float s,p,r,f;

/* s - искомая сумма */

7

Ml:

/♦Члены: p -первый; г - второй; f - i-й*/

8

printf("\п Введите число членов ряда к=");

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

89

9

s c a n f ( " % d " , &k);

 

10

if( k > 2 ) goto М2 ;

!") ;

11

printf("\n Ошибка! It должно быть > 2

12goto M l ;

13М2 printf("\n Первый член ряда p=") ;

14scanf("%f",&p);

15printf("\n Второй член ряда r=");

16scanf( "%f" , & r ) ;

17i = 3 ;

18s = p + r ;

19М: f = p + r;

20s = s + f;

21P = r;

22r = f;

23

i = i + 1;

24if ( i <= It ) goto M;

25printf("\n Сумма членов ряда: %10.3f", s);

26}

Обратите внимание на строки 1 0 -1 2 , где выполняется про­ верка введенного значения к. Программа может правильно ра­ ботать только при к>2, поэтому только в этом случае выполняется переход из строки 10 к метке М2 (строка 13). В противном случае печатается сообщение об ошибке (в стро­ ке 11), и после перехода к метке Ml запрашивается новое зна­ чение к. (Для полного понимания работы программы целе­ сообразно составить трассировочную таблицу, выбрав подхо­ дящие значения к, р, г.)

Результат (текст на экране) может быть, например, таким:

Введите число членов ряда lt=-4 Ошибка! It должно быть > 2 ! Введите число членов ряда к=10

Первый член ряда р=4.0 Второй член ряда г=5.0 Сумма членов ряда: 660 . 0 0 0

Особенность и недостаток программы состоят в том, что она никогда не закончит вычислений, если не ввести допустимого значения к>2.

90

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

2.3. О ператоры ци кла

Три формы операторов цикла. Как и в других языках про­ граммирования, в языке Си существуют специальные средства для организации циклов (операторы циклов), позволяющие уп­ ростить их программирование. В большинстве языков програм­ мирования оператор цикла состоит из двух элементов - заголовка и тела. Тело включает операторы, выполняемые в цикле, заголовок организует циклическое выполнение операто­ ров тела. В соответствии с названием заголовок размещается непосредственно перед телом цикла. В языке Си равноправно используются три разных оператора цикла, обозначаемых соот­ ветственно служебными словами while, for, do.

Циклы while и for построены по схеме

заголовок цикла телоцикла

Цикл do имеет другую структуру - тело цикла как бы обрам­ лено (сверху и снизу) конструкциями, организующими цикли­ ческое выполнение тела. Поэтому говорить о заголовке цикла do в языке Си, по-видимому, некорректно.

Введем форматы перечисленных операторов цикла и приве­ дем примеры их использования. Предварительно отметим, что во всех трех операторах цикла тело цикла - это либо отдельный, либо составной оператор, т.е. последовательность операторов, заключенная в операторные скобки {}. Тело цикла может быть и пустым оператором, т.е. изображаться только как

Цикл while (цикл с предусловием) имеет вид:

while (выражение_условие) телоцикла

В качестве выражения^условия чаще всего используется от­ ношение или логическое выражение. Если оно истинно, т.е. не равно 0, то тело цикла выполняется до тех пор, пока выражение_условие не станет ложным.

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

91

Обратите внимание, что проверка истинности выражения осуществляется до каждого выполнения тела цикла (до каждой итерации). Таким образом, для заведомо ложного выражения_условия тело цикла не выполнится ни разу. Выраже­ ние^условие может быть и арифметическим выражением. В этом случае цикл выполняется, пока значение выражения_условия не равно 0.

Цикл do (цикл с постусловием) имеет вид:

do

телоцикла

while {выражение_условие);

Выражение_условие логическое или арифметическое, как и в цикле while. В цикле do тело цикла всегда выполняется по крайней мере один раз. После каждого выполнения тела цикла проверяется истинность выражения_условия (на равенство 0), и если оно ложно (т.е. равно 0), то цикл заканчивается. В против­ ном случае тело цикла выполняется вновь.

Цикл for (называемый параметрическим) имеет вид:

for {выражение_1; выражение_условие; выражение_3) тело_цикла

Первое и третье выражения в операторе for могут состоять из нескольких выражений, разделенных запятыми. Выраже­ ние_1 определяет действия, выполняемые до начала цикла, т.е. задает начальные условия для цикла; чаще всего это выражение присваивания. Выражение_условие - обычно логическое или арифметическое. Оно определяет условия окончания или про­ должения цикла. Если оно истинно (т.е. не равно 0), то выпол­ няется тело цикла, а затем вычисляется выражение_3. Выражение_3 обычно задает необходимые для следующей ите­ рации изменения параметров или любых переменных тела цик­ ла. После выполнения выражения З вычисляется истинность выраженияусловия, и все повторяется... Таким образом, вы­ раж ение^ вычисляется только один раз, а выражениеусловие и выражение_3 вычисляются после каждого выполнения тела Цикла. Цикл продолжается до тех пор, пока не станет ложным

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