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

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

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

72

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

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

Хотя в разделе, посвященном символам и строковым кон­ стантам (§1.2),упоминалось о возможностях записи управляю­ щих последовательностей и эскейп-последовательностей внутри строк, остановимся еще раз на этом вопросе в связи с формат­ ной строкой. При необходимости вывести на экран (на печать) парные кавычки или апострофы их представляют с помощью соответствующих последовательностей: \" или V, т.е. заменяют парами литер. Обратная косая черта 'V для однократного вывода на экран должна быть дважды включена в форматную строку.

При необходимости вывести символ % его в форматную строку включают дважды: % %.

Программы печати предельных констант. Введенных средств препроцессора и языка вполне достаточно для програм­ мы, выводящей на печать (на экран дисплея) значения констант, определяющие в конкретной системе (для конкретного компи­ лятора) пределы изменения данных разных типов. Таблица стандартных обозначений предельных констант есть в Прило­ жении 2. В главе 1 (§1.3) приведены некоторые из них. Там же, говоря об именованных константах, мы отметили, что среди стандартных заголовочных файлов компилятора всегда есть файлы Iimits.h и float.h, включающие препроцессорное опре­ деление предельных констант. Следующая программа печатает некоторые из значений предельных констант для целых ти­ пов, определенных конкретной реализацией компилятора с языка Си.

#±nclude <stdio.h>

#include <l±m±ts.h> /* Определение предельных целочисленных констант*/

void main( )

{ printf("\nCHAR_BXT=%d", CHAR_BIT) ; printf("\nSCHAR_MIN=%d\t\tSCHAR_MAX=%d",

SCHAR_MIN, SCHAR_MAX); printf("\nUCHAR_MAX=%d", UCHAR_MAX);

printf ("\nINT_MIN=%d\t\tINT_MAX=%d" , XNT MIN,

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

73

INT_MAX); printf("\nLONG_MIN=%ld\tLONG_MAX=%ld",

LONG_MIN, LONG_MAX);

)

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

CHAR_BIT=8

SCHAR_MAX=12 7

SCHAR_MIN=-128

UCHAR_MAX=255

INT_MAX=32767

INT_MIN=-32 768

LONG_MIN=-2147483648

LONG_MAX=2147483647

В вызовах функции printf() нужно обратить внимание на спецификации преобразования. Все константы целочисленные, поэтому используется спецификатор'd'. Для величин типа long потребовался модификатор Т, т.е. константы LONG_MIN и LONG_MAX выводятся с использованием спецификаций пре­ образования %ld. Во всех спецификациях преобразования от­ сутствуют сведения о длине изображения выводимых значений. Количество позиций в изображениях констант зависит от их значений. Управляющие последовательности '\п' и V обеспе­ чивают при выводе соответственно переходы на новые строки и табуляцию.

Для вывода вещественных значений с мантиссой и порядком в форматной строке функции printf() нужно использовать спе­ цификацию %е. Следующая программа выводит на экран зна­ чения некоторых из предельных вещественных констант:

#include <stdio.h>

#include <float.h> /* Определение предельных вещественных констант*/

void main( )

{

printf("\nFLT_EPSILON=%e", FLT_EPSILON); printf("\nDBL_EPSILON=%e", DBL_EPSILON); printf("\nFLT_MIN=%e\tFLT_MAX=%e", FLT_MIN,

FLT_MAX);

printf("\n \t \t \tDBL_MAX=%e", DBL_MAX); printf("\nFLT_MANT_DIG=%d", FLT_MANT_DIG); printf("\nDBL_MANT_DIG=%d", DBL_MANT_DIG);

}

74

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

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

FLT_EPSILON=l.192093е-07 DBL_EPSILON=2.22044бе-16 FLT_MIN=1.175494е-38

FLT_MANT_DIG=2 4

DBL_MANT_DIG=53

FLT_MAX=3.402823е+38 DBL_MAX=1.797693в+308

Отметим применение символа табуляции '\t' для размещения информации, выводимой на дисплей. Больше в использовании функции printf(), кроме спецификации %е, ничего нового нет. Стоит пояснить смысл напечатанных констант. FLT_EPSILON и DBL_EPSILON - максимальные значения типов float и double, сумма каждого из которых со значением 1.0 не отли­ чается от 1.0. Предельные константы FLT_EPSILON ,и DBL_EPSILON называют "машинными нулями" относительно вещественного значения 1.0. FLT_MIN, FLT_MAX и DBL_MAX - предельные значения для вещественных данных. FLT_M ANT_DIG, D B L JM A N T D IG - количество двоич­ ных цифр (бит) в мантиссах соответственно чисел типа float и double.

Применимость вещественных данных. Даже познакомив­ шись с различиями в диапазонах представления вещественных чисел, начинающий программист не сразу осознает различия между типами float, double и long double. Сразу бросается в глаза разное количество байтов, отводимых в памяти для веще­ ственных данных перечисленных типов. На IBM PC:

для float - 4 байта;

для double - 8 байт;

для long double - 10 байт.

Обратив внимание на значения предельных констант, отме­ чают (см. Приложение 2), что максимальные значения, которые можно представить вещественными числами, определены кон­ стантами:

FLT_MAX приблизительно равно 1Е+37 (для float); DBL_MAX приблизительно равно 1Е+308 (для double).

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

75

Затем обращают внимание на количество верных десятичных цифр в мантиссах:

FLTJDIG равно 6 (для float); DBL_DIG равно 10 (для double).

Наконец, оценят минимальные нормализованные числа: FLT_MIN приблизительно равно 1Е-37 (для float); DBL_MIN приблизительно равно 1Е-308 (для double).

Можно и дальше продолжать сравнение констант, перечис­ ленных в описании языка (см. Приложение 2), но нужно сделать выводы относительно применимости вещественных констант и переменных разных типов. По умолчанию все константы, не относящиеся к целым типам, принимают тип double. У про­ граммиста это соглашение часто вызывает недоумение - а не лучше ли всегда работать с вещественными данными типа float и только при необходимости переходить к double или long double? Ведь значения больше 1Е+38 и меньше 1Е-38 встреча­ ются довольно редко.

Следующая программа (предложена С.М.Лавреновым) ил­ люстрирует опасности, связанные с применением данных типа float даже в несложных арифметических выражениях:

#include <stdio.h> void main( )

{

float a, b, c, tl, t2, t3; a=95.0;

b=0.02;

tl= (a+b) * (a+b) ; t2=-2.0*a*b-a*a; t3=b*b; c=(tl+t2)/t3;

printf("\nc=%f\n", c);

}

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

c=2.441406

Если в той же программе переменной а присвоить значение 100.0, то результат будет еще хуже:

76

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

с=0.000000.

Таким образом, запрограммированное с использованием пе­ ременных типа float несложное алгебраическое выражение

(а+Ь)2- ( а 2 +2аЬ)

Ъ1

никак "не хочет" вычисляться и принимать свое явное теорети­ ческое единичное значение.

Если заменить в программе только одну строку, т.е. так оп­ ределить переменные:

double а, Ь, с, tl, t2, t3;

значение выражения вычисляется совершенно точно:

с=1.000000

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

К сожалению, ни double, ни long double не снимают полно­ стью проблем конечной точности представления вещественных чисел в памяти ЭВМ. Существенное различие в порядках значе­ ний операндов арифметических выражений может привести к подобным некорректным результатам и при использовании типов double и long double.

Выделение лексем из текста программы. В главе 1 мы ввели понятие лексемы, перечислили их группы (идентифика­ торы, знаки операций и т.'д.) и определили состав каждой из групп лексем. Первая задача, которую решает компилятор, - это лексический анализ текста программы. В результате лексиче­ ского анализа из сплошного текста выделяются лекси­

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

77

ческие единицы (лексемы). Программисту полезно знать, каки­ ми правилами при этом руководствуется компилятор.

Компилятор просматривает символы (литеры) текста про­ граммы слева направо. При этом его первая задача - выделить лексемы языка. За очередную лексическую единицу принимает­ ся наибольшая последовательность литер, которая образует лексему. Таким образом, из последовательности int_line компи­ лятор не станет выделять как лексему служебное слово int, а воспримет всю последовательность как введенный пользовате­ лем идентификатор.

В соответствии с тем же принципом выражение d+++b трак­ туется как d++ +Ь, а выражение Ь -->с эквивалентно (Ь— )>с.

Следующая программа иллюстрирует сказанное:

#include <stdio.h> void main()

{int n=10,m=2; printf("\nn+++m=%d",n+++m); printf("\nn=%d, m=%d",n,m); printf("\nm;— >n=%d",m— >n); printf("\nn=%d, m=%d",n,m); printf("\nn— >m=%d",n— >m); printf("\nn=%d, m=%d",n,m);

)

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

n+++m=12

n=ll,m=2 m— >n=0

n=ll,m=l n— >m=l

n=10,m=l

Результаты вычисления выражений n+-H-m, n—>m, m—>n полностью соответствуют правилам интерпретации выражений на основе таблицы рангов операций (см. табл. 1.4). Унарные операции ++ и — имеют ранг 2. Аддитивные операции + и - имеют ранг 4. Операции отношений имеют ранг 6.

78

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

2.2. Э лем ентарны е средства програм м и рован ия

Деление операторов языка Си на группы. Если вспомнить вопросы, перечисленные в начале главы 1, то окажется, что мы уже получили ответы на многие из них. Введен алфавит языка и его лексемы; приведены основные типы данных, константы и переменные; определены все операции; рассмотрены правила построения арифметических выражений, отношений и логиче­ ских выражений; описана структура программы; рассмотрены средства вывода из ЭВМ арифметических значений с помощью функции printf( ); определен оператор присваивания.

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

Вернемся вновь к структуре простой программы, состоящей только из одной функции с именем m ain().

директивы препроцессора void m ain()

{определения объектов; исполняемые операторы;

}

Как мы уже договорились, пока нам будет достаточно двух препроцессорных директив ^include <...> и ^define. В качестве определяемых объектов будем вводить переменные и константы базовых типов. А вот об исполняемых операторах в теле функ­ ции нужно говорить подробно.

Каждый исполняемый оператор определяет действия про­ граммы на очередном шаге ее выполнения. У оператора (в от­ личие от выражения) нет значения. По характеру действий раз­ личают два типа операторов: операторы преобразования данных и операторы управления работой программы.

Наиболее типичные операторы преобразования данных - операторы присваивания и произвольные выражения, завер­ шенные символом "точка с запятой":

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

79

±++;

/*Арифметическое выражение

- оператор*/

x*=i;

/*Оператор составного присваивания*/

i=x-4*i; /*Оператор простого присваивания*/

Так как вызов функции является выражением с операцией "круглые скобки" и операндами "имя функции", "список факти­ ческих параметров", к операторам преобразования данных мож­ но отнести и оператор вызова или обращения к функции:

имяфункции (списокфактическихпараметров);

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

Операторы управления работой программы называют управ­ ляющими конструкциями программы. К ним относятся:

составные операторы;

операторы выбора;

операторы циклов;

операторы перехода.

К составным операторам относят собственно составные опе­ раторы и блоки. В обоих случаях это последовательность опера­ торов, заключенная в фигурные скобки. Отличие блока от составного оператора - наличие определений в теле блока. На­ пример, приведенный ниже фрагмент программы - составной оператор:

{

п + + ;

summa+=(float)n;

)

а этот фрагмент - блок:

{

int п=0;

п + + ;

80

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

summa+=(float)п ;

)

Наиболее часто блок употребляется в качестве тела функции. Операторы выбора - это условный оператор (if) и переклю­

чатель (switch).

Операторы циклов в языке Си трех видов - с предусловием (while), с постусловием (do) и параметрический (for).

Операторы перехода выполняют безусловную передачу управления: goto (безусловный переход), continue (завершение текущей итерации цикла), break (выход из цикла или переклю­ чателя), return (возврат из функции).

Перечислив операторы управления программой, перейдем к подробному рассмотрению тех из них, которых будет достаточ­ но для программирования простейших алгоритмов.

Условный оператор имеет сокращенную форму:

if (выражение^условие) оператор;

где в качестве выраженияусловия могут использоваться: арифметическое выражение, отношение и логическое выраже­ ние. Оператор, включенный в условный, выполняется только в случае истинности (т.е. при ненулевом значении) выраже­ нияусловия. Пример:

if (х < 0 && х > -10) х=-х:

Кроме сокращенной формы, имеется еще и полная форма ус­ ловного оператора:

if (выражениеуусловиё) оператор!;

else

оператор_2;

Здесь в случае истинности выражения-условия выполняется только оператор_! при нулевом значении выражения-условия выполняется только оператор_2. Например:

if (х > 0) Ь=х ;

else Ь=-х;

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

81

Оператор в сокращенном варианте оператора if, и опера­ тор_1 и оператор_2 в полном операторе if могут быть как от­ дельными, так и составными операторами.

Несмотря на традиционность условного оператора, проиллю­ стрируем его выполнение схемами (рис. 2.2).

Итак, что в условных операторах в качестве любого из опе­ раторов (после условия или после else) может использоваться

6

Рис. 2.2. Схемы условных операторов (выражение-условие - условие после if): а - сокращенная форма; 6 - полная форма

составной оператор. Например, при решении алгебраического уравнения 2-й степени ах2+Ьх+с=0 действительные корни име­ ются только в случае, если дискриминант (Ь2-4ас) неотрицате­ лен. Следующий фрагмент программы иллюстрирует использование условного оператора при определении действи­ тельных корней х1э х2 квадратного уравнения:

d=b*b - 4*а*с;

/* d - дискриминант */

i f (d>=0.0)

 

{ ' xl=(-b+sqrt(d))/2/а; х2=(-b-sqrt(d))/2/а;

6 - 3 1 2 4

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