книги / Программирование на языке Си
..pdf22 |
Программирование на языке Си |
.Таким образом, количество байтов, выделяемое в памяти ЭВМ для представления значения строки, ровно на 1 больше, чем число символов в записи этой строковой константы:
"Эта строка занимает в памяти ЭВМ 43 байта." "Строка в 18 байт."
При работе с символьной информацией нужно помнить, что длина константы 'Р равна 1 байту, а длина строки "F" равна 2 байтам.
При записи строковых констант возможно размещение одной константы в нескольких строках текстового файла с програм мой. Для этого используется следующее правило.
Если в последовательности символов (литер) константы встречается литера 'V, за которой до признака '\п* конца строки текстового файла размещены только пробелы, то эти пробелы вместе с символом 'V и окончанием *\п' удаляются, и продолже нием строковой константы считается следующая строка текста. Например, следующий текст представляет одну строковую константу:
"Шалтай-Болтай \ сидел на стене."
В программе эта константа будет эквивалентна такой:
"Шалтай-Болтай |
сидел.на стене." |
Начальные (левые) пробелы в продолжении константы на новой строке не удаляются, а считаются входящими в строко вую константу.
Две строковые константы, между которыми нет других раз делителей, кроме обобщенных пробельных символов (пробел, табуляция, конец строки и т.д.); воспринимаются как одна стро ковая константа. Таким образом,
"Шалтай-Болтай" " свалился во сне."
воспринимается как одна константа:
"Шалтай-Болтай свалился во сне."
Глава 1. Базовые понятия языка |
23 |
Тем же правилам подчиняются и строковые константы, раз мещенные на разных строках. Как одна строка будет воспринята последовательность
"Вся королевская " "конница, "
"вся' королевская " "рать"
Эти четыре строковые константы эквивалентны одной:
"Вся королевская конница, вся королевская рать”
Обратите внимание, что в результирующую строку здесь не включаются начальные пробелы перед каждой константойпродолжением.
1.3. П ерем енны е и им енованны е константы
Переменная как объект. Одним из основных понятий языка Си является объект - именованная область памяти. Частный случай объекта - переменная. Отличительная особенность пе ременной состоит в возможности связывать с ее именем различи ные значения, .совокупность которых определяется типом переменной. При задании значения переменной в соответст вующую ей область памяти помещается код этого значения. Доступ к значению переменной наиболее естественно обеспечи вает ее имя, а доступ к участку памяти возможен только по его адресу. О взаимосвязях имен и адресов будет подробно гово риться в главе, посвященной указателям и работе с памятью ЭВМ. Для целей первых глав будет вполне достаточно интер претировать понятие переменной как пару "имя - значение".
Определение переменных. Каждая переменная перед ее .ис пользованием в программе должна быть определена, т.е. для переменной должна быть выделена память. Размер участка па мяти, выделяемой длй переменной, и интерпретация содержи мого зависят от типа, указанного в определении переменной.
24 |
Программирование на языке Си |
В соответствии с типами значений, допустимых в языке Си, рассмотрим символьные, целые и, вещественные переменные автоматической памяти. О классах памяти (один из которых класс автоматической памяти) будем подробно говорить позже. Сейчас достаточно ввести только переменные автоматической памяти, которые существуют в том блоке, где они определены. В наиболее распространенном случае таким блоком является текст основной (main) функции программьГ.
Простейшая форма определения переменных:
тип списокименпеременных;
где имена переме:нных - это выбранные программистом иден тификаторы, которые в списке разделяются запятыми; тип - один из уже упоминаемых (в связи с константами) типов.
Определены целочисленные типы (перечислены в порядке неубывания длины внутреннего представления):
char - целый длиной не менее 8 бит;
short int - короткий целый (допустима аббревиатура
int |
short); |
-целый; |
|
long |
- длинный целый. |
Каждый из целочисленных типов может быть определен ли бо как знаковый signed либо как беззнаковый unsigned (по умолчанию signed).
Различие между этими двумя типами - в правилах интерпре тации старшего бита внутреннего представления. Спецификатор signed требует, чтобы старший бит внутреннего представления воспринимался как знаковый; unsigned означает, что старший бит внутреннего представления входит в код представляемого числового значения, которое считается в этом случае беззнако вым. Выбор знакового или беззнакового представления опреде ляет предельные значения, которые можно представить с помощью описанной переменной. Например, на IBM PC пере менная типа unsigned int позволяет представить числа от 0 до 65535, а переменной типа signed int (или просто int) соответст
Глава 1. Базовые понятия языка |
25 |
вуют значения в диапазоне от -32768 до +32767. Чтобы глубже понять различие между целой величиной и целой величиной без знака, следует обратить внимание на результат выполнения унарной операции (минус) над целой величиной и целой ве личиной без знака. Для целой величины результат очевиден и тривиален. Результатом при использовании целой величины без знака является
2П - (значение величины без знака),
где п - количество разрядов, отведенное для представления ве личины без знака.
Например, если переменная к типа int равна 16, то значени ем -к будет -16. Если переменная Ь типа unsigned int равна 16, то значением -Ь на IBM PC является 65520.
По умолчанию, при отсутствии в качестве префикса клю чевого слова unsigned любой целый тип считается знаковым (signed). Таким образом, употребление совместно со служеб ными словами char, short, int, long префикса signed излишне. Допустимо отдельное использование обозначений (специфи каторов) "знаковости". При этом
signed эквивалентно signed int; i> unsigned эквивалентно unsigned int.
Примеры определений целочисленных переменных:
char symbol, сс; unsigned char code; int number, row;1
unsigned long 1ong_number;
Обратите внимание на необходимость символа "точка с запя той" в конце каждого определения.
Стандартом языка введены следующие вещественные типы: float - вещественный одинарной точности;
double - вещественный удвоенной точности; long double- вещественный максимальной точности.
Значения всех вещественных типов в ЭВМ представляются с "плавающей точкой", т.е. с мантиссой и порядком, как было
26 |
Программирование на языке Си |
рассмотрено при определении констант (§1.2). Примеры опре делений вещественных переменных:
float х, X, ссЗ, pot_8 ; double a, Stop, В4;
Предельные значения переменных. Предельные значения констант (и соответствующих переменных) разработчики ком пиляторов вправе выбирать самостоятельно исходя из аппарат ных возможностей компьютера. Однако при такой свободе выбора стандарт языка требует, чтобы для значений типа short
иint было отведено не менее 16 бит, для long - не менее 32 бит. При этом размер long должен быть не менее размера int, a int - не менее short. Предельные значения арифметических констант
ипеременных для большинства компиляторов, реализованных на IBM PC, приведены в табл. 1.3.
Предельные значения вещественных переменных совпадают
спредельными значениями соответствующих констант (см., на пример, табл. 1.2).
|
|
|
Т аблица 1.3 |
|
Основные типы данных |
||
Тип данных |
Размер, бит |
Диапазон значений |
|
Unsigned char |
8 |
|
0... 255 |
char |
8 |
|
-128... 127 |
enum |
16 |
|
-32768... 32767 |
unsigned int |
16 |
|
0... 65535 |
short int (short) |
16 |
|
-32768... 32767 |
unsigned short |
16 |
|
0... 65535 |
int |
16 |
|
-32768... 32767 |
unsigned long |
32 |
|
0... 4294967295 |
long |
32 |
|
-2147483648 ...2147483647 |
float |
32 |
, |
3.4Е-38... 3.4Е+38 |
donble |
64 |
|
1.7Е-308... 1.7Е+308 |
long donble |
80 |
|
3.4Е-4932... 1.1Е+4932 |
Глава 1. Базовые понятия языка |
27 |
Предельные значения целочисленных переменных совпада ют с предельными значениями соответствующих констант (см. табл. 1.1). Табл. 1.3 содержит и предельные значения для тех типов, которые не включены в табл. 1.1.
Требования стандарта отображают таблицы Приложения 2.
Инициализация переменных. В соответствии с синтакси сов языка переменные автоматической памяти после определе ния по умолчанию имеют неопределенные значения. Надеяться на то, что они равны, например, 0, нельзя. Однако переменным можно присваивать начальные значения, явно указывая их в оп ределениях:
тип имя_ переменной=начальное значение;
Этот прием назван инициализацией. В отличие от присваи вания, которое, осуществляется в процессе выполнения про граммы, инициализация выполняется при выделении для переменной участка памяти. Примеры определений с инициали зацией:
float pi=3.1415, сс=1.23; unsigned int year=1997;
Именованные константы. В языке Си, кроме переменных, могут быть определены константы, имеющие фиксированные названия (имена). В качестве имен констант используются про извольно выбираемые программистом идентификаторы, не сов падающие с ключевыми словами и с другими именами объектов. Традиционно принято, что для обозначений констант выбирают идентификаторы из больших букв латинского алфа вита и символов подчеркивания. Такое соглашение позволяет при просмотре большого текста программы на языке Си легко отличать имена переменных от названий констант.
Первая возможность определения именованных констант была проиллюстрирована в §1.2, посвященном константам. Это перечисляемые константы, вводимые с использованием слу жебного слова enum.
Вторую возможность вводить именованные константы обес печивают определения такого вида:
28 |
Программирование на языке Си |
const тип имя_константы=значение константы;
Здесь const - квалификатор типа, указывающий, что опреде ляемый объект имеет постоянное значение, т.е. доступен только для чтения; тип - один из типов объектов; имя константы - идентификатор; значение константы должно соответствовать •?е типу. Примеры:
const double Ё=2.718282; const long №99999999; const F=765;
В последнем определении тип константы не указан, по умол чанию ей приписывается тип int.
Третью возможность вводить именованные константы обес печивает препроцессорная директива
#define имя константы значение константы
Обратите внимание на отсутствие символа "точка с запятой" в конце директивы. Подробному рассмотрению директивы #define будут посвящены два параграфа главы 3. Здесь мы только упоминаем о возможности с ее помощью определять именованные константы. Кроме того, отметим, что в конкрет ные реализации компиляторов с помощью директив #define включают целый, набор именованных констант с фиксирован ными именами (см. главу 3 и Приложение 2).
Отличие определения именованной константы
const double Е=2.718282;
от определения препроцессорной константы с таким же значе нием
ffdefine EULER 2.718282
состоит внешне в том, что в определении константы Е явно за дается ее тип, а при препроцессорном определении константы EULER ее тип определяется "внешним видом" значения кон станты. Например, следующее определение
#define NEXT 'Z'
Глава 1. Базовые понятия языка |
29 |
вводит обозначение NEXT для символьной константы 'Z'. Это соответствует такому определению:
const char NEXT = 'Z';
Однако различия между обычной именованной константой и препроцессорной константой, вводимой директивой #define, гораздо глубже и принципиальнее. До начала компиляции текст программы на языке Си обрабатывается специальным компо нентом транслятора - препроцессором. Если в тексте встречает ся директива
ffdefine EULER |
2.718282 |
а ниже ее в тексте используется имя константы EULER, напри |
|
мер, в таком виде: |
' |
double mix = EULER; d = alfa*EULER;
то препроцессор заменит каждое обозначение EULER на ее зна чение и сформирует такой текст:
double mix = 2.718282; d = alfa*2.718282;
Далее текст от препроцессора поступает к компилятору, ко торый уже "и не вспомнит" о существовании имени EULER, ис пользованного в препроцессорной директиве #define. Конс танты, определяемые на препроцессорном уровне с помощью директивы #define, очень часто используются для задания раз меров массивов, что будет продемонстрировано позже.
Итак, основное отличие констант, определяемых препроцессорными директивами ^define, состоит в том, что эти константы вводятся в текст программы до этапа ее компиляции. Специаль ный компонент транслятора - препроцессор обрабатывает ис ходный текст программы, подготовленный программистом, и делает в этом тексте замены и подстановки. Пусть в исходном тексте встречается директива:
#de£ine ZERO 0.0
30 |
Программирование на языке Си |
Это означает, что каждое последующее использование в тек сте программы имени ZERO будет заменяться на 0.0.
Рис. 1.1 иллюстрирует некоторые принципы работы препро цессора. Его основное отличие от других компонентов трансля тора - обработка программы выполняемся только на уровне ее текста. На входе препроцессора - текст с препроцессорными директивами, на выходе препроцессора - модифицированный текст без препроцессорных директив. Этот выходной модифи цированный текст изменен по сравнению с входным текстом за счет выполнения препроцессорных директив, но сами препроцессорные директивы в выходном тексте отсутствуют. Полно стью все препроцессорные директивы будут рассмотрены позже в главе 3. В связи с именованными константами здесь рассмат ривается только одна из возможностей директивы ^define - простая подстановка.
Текст до препроцессора (исходный текст программы):
«define PI 3.141593 «define ZERO 0.0
if (r>ZERO)/* Сравнение с константой ZERO ♦/ /* Длина окружности радиуса г:*/
D=2*PI*r;
Текст после препроцессора:
if (г>0.0)/* Сравнение с константой ZERO */ /♦Длина окружности радиуса г:♦/ D=2*3.141593*r;
Рис. 1.1. Обработка текста программы препроцессором
Глава 1. Базовые понятия языка |
31 |
Имена PI и ZERO (см. рис. 1.1) после работы препроцессора заменены в тексте программы на определенные в двух директи вах ^define значения (3.141593 и 0.0). '
Обратите внимание, что подстановка не выполняется в ком ментариях и в строковых константах. В примере на рис. 1.1 идентификатор ZERO остался без изменений в комментарии (/* Сравнение с константой ZERO */).
Именно с помощью набора именованных препроцессорных констант стандарт языка Си рекомендует авторам компиляторов определять предельные значения всех основных типов данных. Для этого в языке определен набор фиксированных имен, каж дое из которых является именем одной из констант, опреде ляющих то или иное предельное значение. Например:
FLT MAX |
- максимальное число с плавающей точкой |
|
типа float; |
C H A R B IT - количество битов в байте; |
|
ШТ_МЕЧ |
- минимальное значение для данных типа in t |
Общий список стандартных препроцессорных именованных констант для арифметических данных дан в Приложении 2.
Чтобы использовать в программе указанные именованные препроцессорные константы, недостаточно записать их имена в программе. Предварительно в текст программы необходимо включить препроцессорную директиву такого вида:
^include <имя_заголовочного_файпа>
где в качестве имени_заголовочного_файла подставляются:
limits.h |
- |
для данных целых типов;' |
floath |
- |
для вещественных данных. |
В заголовочный файл Iimits.h авторы компилятора помести ли набор препроцессорных директив, среди которых есть такие (приведены значения в шестнадцатеричном виде для Turbo С):
#define CHAR_BIT 8 ♦define SHRT_MAX 0x7FFF
♦define LONG MAX 0x7FFFFFFFL