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

Костюк - Основы программирования

.pdf
Скачиваний:
138
Добавлен:
30.05.2015
Размер:
1.3 Mб
Скачать

181

Среди всех базовых типов void является пустым или неопределенным ти­ пом. Характеристики целочисленных типов приведены в табл. Б.1, а вещественных типов – в табл. Б.2.

 

 

Таблица Б.1

 

 

 

 

 

Тип

Длина, байт

Диапазон значений

char

 

1

–128 … 127

unsigned char

1

0 … 255

int

 

2

–32768 … 32767

unsigned

2

0 … 65535

long

 

4

–2147483648 … 2147483647

unsigned long

4

0 … 4294967295

Таблица Б.2

Тип

Длина, байт

Диапазон значений

Число верных

десятичных цифр

 

 

 

float

4

1,5∙10–45 … 3,4∙1038

7–8

double

8

5,0∙10–324 … 1,7∙10308

15–16

long double

10

3,4∙10–4932 … 1,1∙104932

19–20

Константы. Целочисленная константа записывается в виде нескольких десятич­ ных цифр, впереди может быть записан знак (+ или ), например, –1003, +10,

32000, 100000000. Константа, начинающаяся с нуля, считается восьмеричной. Константа, начинающаяся со знаков 0x или 0X, считается шестнадцатеричной, кроме десятичных цифр в запись такой константы могут входить буквы от a до z (или от A до Z). Этим буквам соответствуют цифровые значения от 10 до 15.

Пример константы 511 в восьмеричном виде: 0777, в шестнадцатеричном виде: 0x1FF. Тип константы (int или long) определяется ее значением. Например, тип константы 100 или 10000 int, а тип константы 100000 long. Если необ­

ходимо, тип константы можно задать явно приписыванием в ее конце буквенного

символа. Символ L (или l) означает константу типа long, U (или u) – константу типа unsigned, два символа LU (или UL, lu, ul) – константу типа unsigned long.

Константа типа char может записываться в виде символа, взятого в одиночные кавычки, например: ’a’, ’+’, ’[’, ’Щ’, ’ц’. Некоторые символы записываются так, как представлено в табл. Б.3.

Вещественная константа записывается в виде мантиссы, перед которой может быть знак (+ или ), после мантиссы может быть записан порядок числа (со знаком

или без него). Мантисса записывается как целое число или число с десятичной точ­

кой. Порядок записывается так: буква e, затем целое число (значение порядка). Примеры: –13e-10, +1e6, 3.14, 5.99e+22. По умолчанию тип вещественной

константы double. Если необходимо, тип константы можно задать явно приписы­

182

ванием в ее конце буквенного символа. Символ L (или l) означает константу типа long double, F (или f) – константу типа float.

Таблица Б.3

 

 

 

Название символа

Обозначене

Символ новой строки (NL)

\n

Горизонтальная табуляция (NT)

\t

Перевод формата (FF)

\f

Обратная косая черта (\)

\\

 

Одиночная кавычка ( )

\’

Символ с восьмеричным кодом 0ddd

\0ddd

Символ с шестнадцатеричным кодом 0xdd

\0xdd

Пустой символ с кодом 0

\0

Строка (строковая константа) записывается как последовательность символов, заключенная в двойные кавычки. Среди символов строки могут быть представленные кодами из табл. Б.3. В памяти компьютера строка занимает 1 байт на каждый символ, кроме того, в конце строки добавляется еще один байт с кодом 0 (признак конца). Например, строка ”” содержит 0 символов и занимает в памяти 1 байт. Две стро­ ковые константы, записанные подряд или через пробелы (в том числе с переходом на другую строку текста в программе) считаются одной целой строкой.

Понятие <описание типа> задает новое имя типу. Описание, начинающееся со слова typedef, определяет имя для типа, состоящего из нескольких описателей. В

последующем заданное имя используется при описании объектов этого типа. Описа­ ние, начинающееся со слова struct, определяет имя для типа, являющегося струк­

турой (записью). При описании объектов такой тип записывается как struct <имя структуры>. Поля структуры записываются аналогично описанию объектов, каждое поле имеет индивидуальное имя и тип.

Понятие <описание объектов> записывается в виде типа и списка (через запя­ тую) описываемых объектов. Объект задается именем, перед которым могут стоять несколько звездочек, а после него – квадратные скобки. При выполнении программы описание объектов действует как оператор, выделяющий для них память. После объекта через равенство может быть записано значение, которое для объекта-пере­ менной присваивается после выделения для него памяти. Звездочка перед объектом означает, что описываемый объект является ссылкой (указателем). Например, две звездочки означают, что объект является указателем на указатель.

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

183

квадратными скобками состоит в том, что в первом случае выделяется память для объекта-указателя (обычно это 4 байта), а во втором случае дополнительно выделяет­ ся память для всех элементов массива. В Си нельзя описать обычный многомерный массив (как, например, в Паскале), но можно описать массив массивов, массив мас­ сивов массивов и т.д. Если объект-массив является константой, то число элементов внутри квадратных скобок можно не указывать, так как значения этих элементов в описании указываются непосредственно.

Понятие <указатель на функцию> – особое описание объекта – указателя на функцию с определенным в описании числом и типом аргументов функции и типом вырабатываемого ею значения. В дальнейшем этому объекту можно присвоить ссыл­ ку на настоящую функцию, описанную в программе. Такой объект может быть опи­ сан как массив, а также как константа.

Понятие <описание функции> записывается в виде заголовка и блока либо в виде заголовка и точки с запятой. В заголовке задается тип вырабатываемого функ­ цией значения (которое может быть и пустым), а также типы и обозначения аргумен­ тов. Слово void означает, что аргуменов у функции нет, однако круглые скобки в вызове такой функции все равно пишутся. В конце списка аргументов в описании мо­ жет записываться многоточие (три точки). Это означает, что вместо точек при вызове может находиться произвольное количество любых параметров.

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

Функция main записывается или как не имеющая аргументов, или с двумя ар­ гументами в соответствии с прототипом: void main(int n,char *P[]). Во

втором случае исполняемый модуль программы можно вызывать из командной стро­ ки операционной системы, задавая после имени программы текстовые параметры, разделенные пробелами. Тогда значение n будет равно количеству этих параметров плюс 1. Доступ к параметрам – через указатель на массив строк символов P. При этом P[0] – указатель на строку символов, в которой записан полный путь к файлу исполняемого модуля, начиная от имени диска и включая имя файла. P[1] – указа­ тель на строку символов – первый параметр в командной строке, P[1] – указатель на строку символов – второй параметр и т.д.

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

184

объекты описаны с описателем static, тогда память не аннулируется). Имена объектов локализуются внутри того блока, где они описаны.

Понятие <выражение> записывается в виде совокупности имен объектов и опе­ раций. Каждая из операций обозначается одним или несколькими символами. Опера­ ции классифицируются по приоритетам (рангам) от высшего (1-го) до низшего (15го), список стандартных операций приведен в табл. Б.4.

 

 

 

 

 

 

Таблица Б.4

 

 

 

 

 

 

 

 

 

 

 

 

 

Приори­

 

 

 

 

 

Операции

 

Порядок

тет

 

 

 

 

 

 

исполнения

 

 

 

 

 

 

 

 

 

1

()

[]

-> .

 

 

 

 

2

!

~

+

-

++

--

&

* (<тип>)

sizeof

3

*

/

%

 

 

 

 

 

 

4

+

-

 

 

 

 

 

 

 

5

<<

>>

 

 

 

 

 

 

 

6

<

<=

>=

>

 

 

 

 

 

7

==

!=

 

 

 

 

 

 

 

8

&

 

 

 

 

 

 

 

 

9

^

 

 

 

 

 

 

 

 

10

|

 

 

 

 

 

 

 

 

11

&&

 

 

 

 

 

 

 

 

12

||

 

 

 

 

 

 

 

 

13

? :

 

 

 

 

 

 

 

14

=

*=

/=

%=

+=

-=

&=

^= |= <<=

>>=

15

,

 

 

 

 

 

 

 

 

Приоритеты операций определяют порядок их исполнения в выражении: вначале исполняются операции 1-го приоритета, затем 2-го и т.д. Если в выражении имеется несколько операций с одинаковым приоритетом, то порядок их исполнения может быть слева-направо () или справа-налево (), см. табл. Б.4. Операциии подразделя­ ются также на унарные (с одним операндом), бинарные (с двумя) и тернарные (с тре­ мя операндами). Рассмотрим смысл стандартных операций.

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

Операция квадратные скобки [] – бинарная, в ней 1-й операнд – имя массива, 2-й – индексное выражение (целочисленное), результат – элемент массива.

185

Операции выбора поля структуры – косвенный выбор -> и прямой выбор (точка). Обе операции бинарные, записаваются в виде:

1)<имя указателя на структуру> -> <имя поля структуры>

2)<имя структуры> . <имя поля структуры>

В обеих операциях результат – выбранное поле в структуре.

Унарные операции с приоритетом 2 (! ~ + - & * (<тип>) sizeof) яв­ ляются префиксными, т.е. записываются перед операндом.

Операция ! – логическое отрицание. Если операнд равен 0, т.е. ложь, то ре­ зультат равен 1, т.е. истина. Если операнд не равен 0, то результат равен 0.

Операция ~ – поразрядное инвертирование двоичного представления целочис­ ленного операнда, рассматриваемого как битовый вектор.

Операции + и - – унарные плюс и минус соответственно. Результат – значе­ ние операнда (+) или значение операнда с противоположным знаком (-).

Операция & – получение адреса операнда, который должен быть переменной. Операция * – доступ к объекту, на который указывает операнд- указатель.

Операция преобразования к указанному типу (<тип>). Операнд может быть любым целочисленным или вещественным типом, тип результата определяется опе­

рацией. При выполнении операции возможна потеря точности (например, при преоб­ разовании от типа double к типу float), или потеря значения (например, при

преобразовании значения 1000 к типу char, из-за нехватки разрядности). Операн­ ду типа void можно приписать любой тип, хотя никакого преобразования при этом не производится. Если операция записана в виде (void), то операнд как бы «теря­ ет» свой тип без какого-либо преобразования.

Операция sizeof – вычисление размера (в байтах) операнда. Возможны два формата операции: sizeof <выражение> или sizeof (<тип>). Операция не вы­ числяет значение выражения, она лишь определяет его тип.

Унарные операции с приоритетом 2 (++ --) могут применяться в префиксном или в постфиксном вариантах. В первом варианте они записываются перед, а во втором варианте – после операнда. Операнд должен быть переменной числового ти­ па. Результат операции – та же самая переменная, значение которой либо увеличено на 1 (операция ++), либо уменьшено на 1 (операция --). В префиксном варианте переменная-операнд вначале изменяется, а затем используется. В постфиксном – вначале используется, а затем изменяется.

Тернарная операция с приоритетом 13 (? :), называется условной операцией.

Она записывается двумя отдельными символами с тремя операндами в виде: <выражение-1> ? <выражение-2> : <выражение-3>

Эта операция выполняется следующим образом. Вначале вычисляется <выраже­ ние-1>. Если его значение не равно 0, то вычисляется <выражение-2>, в противном

186

случае вычисляется <выражение-3>. Результатом операции является значение 2-го или 3-го выражения.

Остальные операции из табл. Б.4 являются бинарными, они записываются меж­ ду двумя операндами.

Мультипликативные операции с приоритетом 3 (* / %). Операции умноже­ ния (*) и деления (/) могут иметь любые числовые операнды. Тип результата опреде­ ляется типом операнда с наибольшей разрядностью. Если хотя бы один из операндов вещественный, результат также будет вещественным. Если же оба операнда целочис­ ленные, то результат также будет целочисленным, причем в операции деления оста­ ток от деления отбрасывается. Операция остатка от деления (%) требует целочис­

ленных операндов. При целочисленных a, b и b ≠ 0 справедливо тождество:

(a/b)*b + (a%b) = a.

Аддитивные операции с приоритетом 4 – сложение (+) и вычитание (-) –могут иметь любые числовые операнды и даже указатели. Тип результата определяется ти­ пом операнда с наибольшей разрядностью. Если хотя бы один из операндов веще­ ственный, результат также будет вещественным. Если же оба операнда целочислен­ ные, то результат также будет целочисленным. Если первый операнд указатель, а второй – целочисленный, то результат будет указателем.

Операции сдвига с приоритетом 5 (<< >>) требуют целочисленных операн­ дов. Первый операнд, рассматриваемый как битовый вектор, сдвигается влево (<<) или вправо (>>) на число бит, равных второму операнду. При сдвиге влево освобо­

дившиеся позиции заполняются нулями, при сдвиге вправо – также нулями, если пер­ вый операнд имеет тип с описателем unsigned, или старшим битом в противном

случае.

Операции сравнения с приоритетом 6 (меньше <), (больше >), (меньше или равно <=), (больше или равно >=) и с приоритетом 7 (равно ==), (не равно !=) мо­

гут иметь числовые операнды, а последние две операции, кроме того, – операндыуказатели. Результат сравнения – целое число 0 (ложь) или 1 (истина).

Поразрядные операции над целочисленными операндами (одинаковой длины), рассматриваемыми как битовые векторы – конъюнкция «И» (&) с приоритетом 8, ис­ ключающее «ИЛИ» (^) с приоритетом 9, дизъюнкция «ИЛИ» (|) с приоритетом 10. Результат – битовый вектор.

Логические операции над целочисленными операндами – конъюнкция «И» (&&) с приоритетом 11, дизъюнкция «ИЛИ» (||) с приоритетом 12. Значение операнда,

равное нулю, трактуется как ложь, а не равное нулю – как истина. Результат – целое число 0 (ложь) или 1 (истина).

Операции присваивания с приоритетом 14 (= *= /= %= += -= &= ^= |= <<= >>=) в качестве первого операнда должны иметь переменную, а второго опе­

187

ранда – значение. При выполнении простого присваивания (=) значение переменной (левого операнда) приобретает значение правого операнда. Типы операндов должны совпадать или допускать преобразование от типа второго операнда к типу первого. В частности, целочисленный тип может быть преобразован к вещественному, может измениться длина битового представления целочисленного типа и др.

Остальные операции присваивания совмещают обычную бинарную операцию (<опер>) с присваиванием. Запись a <опер>= b эквивалентна a = a <опер> b.

Операция запятая (,) разделяет последовательно (слева-направо) вычисляющи­ еся выражения. Результатом операции становится значение последнего вычисленно­ го выражения.

Условный оператор if (<выражение>)<оператор-1> [ else <оператор-2>] реализует ветвящийся алгоритм. Он выполняется следующим образом. Вначале вы­ числяется <выражение>. Если вычисленное значение не равно 0, то выполняется

<оператор-1>, в противном случае выполняется <оператор-2> (если записано слово else и сам <оператор-2>).

Оператор switch (<выражение>)<блок> реализует ветвящийся алгоритм. Конструкция switch реализует выбор для исполнения одной из групп операторов,

записанных внутри блока. Полную запись конструкции switch можно представить

в виде:

 

switch (<выражение>)

 

{ case <константа-1>: <список операторов-1> break;

case <константа-2>: <список операторов-2>

break;

. . .

break;

case <константа-n>: <список операторов-n>

default: <список операторов-n+1>

}

При выполнении конструкции switch вначале вычисляется <выражение>.

Если вычисленное значение совпало с <константой-i>, то выполняется <список опе­

раторов-i>, а после него – оператор break, который приводит к завершению всей конструкции switch. Если же вычисленное значение не совпало ни с одной из

<констант-i>, то выполняется <список операторов-n+1>.

В конструкции switch допускается несколько подряд идущих меток case с различными константами. Допускается также отсутствие метки default, тогда при

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

Оператор while (<выражение>)<оператор> реализует циклический алго­ ритм. При его исполнении вычисляется <выражение> и проверяется, не равно ли оно

188

нулю. Если равно, то выполнение цикла заканчивается, если нет, то выполняется <оператор>, снова вычисляется <выражение> и т.д.

Оператор for(<выражение-1>;<выражение-2>;<выражение-3>)<оператор> также реализует циклический алгоритм. При его исполнении вначале вычисляется <выражение-1>. Затем вычисляется <выражение-2> и проверяется, не равно ли оно нулю. Если равно, то выполнение цикла заканчивается, если нет, то выполняется <оператор>, после него – <выражение-3>, затем снова вычисляется <выражение-2> и т.д.

Оператор do <блок> while (<выражение>) реализует циклический алгоритм с проверкой условия в конце. При его исполнении вначале выполняются операторы, входящие в <блок>, и только после этого вычисляется <выражение> и проверяется, не равно ли оно нулю. Если равно, то выполнение цикла заканчивается, если нет, то вы­ полняется <блок>, снова вычисляется <выражение> и т.д.

Оператор return [<выражение>] является последним выполняемым опера­

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

значение <выражения> становится значением, вычисленным при этом вызове функ­ ции. <Выражение> в записи оператора отсутствует, если тип значения, вырабатывае­ мого функцией, описан как void.

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

Б.2 Препроцессор Си

Вся программа, записанная на языке Си, может быть разделена на текстовые файлы, каждый из которых с точки зрения синтаксиса Си также является програм­ мой. Если таких файлов несколько, то целесообразно создать специальный файлпроект, в трансляторе Turbo C он имеет расширение prj. Файл-проект содержит перечисление имен файлов программы, каждое имя записывается в отдельной строч­ ке. После трансляции всех исходных файлов, перечисленных в файле-проекте, полу­ ченный набор объектных модулей (файлов с расширением obj) обрабатывается ре­ дактором связей. В результате создается исполняемый модуль, в него редактор свя­ зей включает также объектные модули стандартных функций, вызывамых из исход­ ной программы.

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

189

программе. Каждая препроцессорная директива записывается в отдельной строчке и начинается с символа #.

Директива #define записывается в нескольких формах. Запись ее в виде: #define <имя> [<символы>]

заменяет все последующие вхождения идентификатора с указанным <именем> на <символы>. Этой директивой, в частности, можно задать именованную константу, тогда <символы> должны представлять собой запись значения этой константы. Если <символы> отсутствуют, то директива определяет препроцессорную перемен­ ную. Отменяет такую переменную директива #undef <имя>. Другая форма записи

директивы:

#define <имя макроса>(<список параметров>) <текст макроса>

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

Директива #include <имя файла> вставляет текст из указанного файла. Имя файла может быть заключено в символы <> (угловые скобки) или в символы ”” (двойные кавычки). Если имя файла заключено в угловые скобки, то этот файл нахо­ дится в стандартных каталогах (стандартный файл), а если в кавычках – то в текущем рабочем каталоге, обычно это файл, созданный программистом.

Директивы ветвлений #if, #ifdef, #ifndef, #else, #endif, #elif

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

этих директив:

#if <условие-1> <текст-1>

[#elif <условие-2> <текст-2>

. . .

[#elif <условие-n>

<текст-n>] . . . ] [#else

<текст-n+1>]

#endif

Тексты могут состоять из нескольких строк, при этом в них могут присутство­ вать другие препроцессорные директивы. Все условия являются константными выра­

190

жениями, т.е. в качестве элементарных операндов в них должны использоваться константы. В условиях может использоваться унарная препроцессорная операция defined, операндом которой служит имя препроцессорной переменной. Значение

выражения defined <имя> является 1 (истина), если указанная переменная ранее была определена директивой #define <имя>, и 0 (ложь) в противном случае.

Препроцессорная обработка такой кострукции состоит в следующем. Вначале

вычисляется <условие-1>. Если оно не равно нулю, в программу вставляется <текст-

1>, иначе вычисляется <условие-2> и т.д. Если все условия оказались равными

0,

то вставляется <текст-n+1>.

 

Директива #ifdef <имя> эквивалентна директиве #if defined <имя>,

а

директива #ifndef <имя> эквивалентна директиве #if !defined <имя>. Эти директивы позволяют записывать условия в более компактной форме.

Директива #error <сообщение> выдает заранее заготовленное сообщение при трансляции.

Директива #pragma <параметры> выполняет действия, предусмотренные в конкретном трансляторе Си. В частности, в трансляторе Turbo C этой директивой можно задавать параметры трансляции.

Директива # (пустая директива) не выполняет никаких действий.

Кроме директив, препроцессору доступны следующие стандартные препроцес­ сорные имена-константы, определенные по умолчанию (в этих именах два первых и два последних символа – символы подчеркивания):

_ _LINE_ _ – номер текущей обрабатываемой строки исходного файла (нумера­

ция с 1);

_ _FILE_ _ – строка символов – имя транслируемого файла (или файла, встав­ ляемого в данный момент в программу директивой #include);

_ _DATE_ _ – строка символов, в которой записана дата трансляции программы

в форме: "месяц день год";

_ _TIME_ _ – строка символов, в которой записано время трансляции програм­ мы в форме: "часы: минуты: секунды".

В конкретном трансляторе Си могут быть определены и другие препроцессорные имена-константы.

Б.3 Стандартные функции

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