Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Подбельский Фомин_Программирование на языке СИ_...doc
Скачиваний:
360
Добавлен:
10.08.2019
Размер:
53.81 Mб
Скачать

Предельные значения переменных.

Предельные значения переменных. Предельные значения констант (и соответствующих переменных) разработчики компиляторов вправе выбирать самостоятельно исходя из аппаратных возможностей компьютера. Однако при такой свободе выбора стандарт языка требует, чтобы для значений типа short и int было отведено не менее 16 бит, для long - не менее 32 бит. При этом размер long должен быть не менее размера int, a int –не менее short. Предельные значения арифметических констант и переменных для большинства компиляторов, реализованных на IBM PC, приведены в табл. 1.3.

Таблица 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

double

64

1.7Е-308... 1.7Е+308

long double

80

3.4Е-4932... 1.1Е+4932

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

Предельные значения целочисленных переменных совпадают с предельными значениями соответствующих констант (см. табл. 1.1). Табл. 1.3 содержит и предельные значения для тех типов, которые не включены в табл. 1.1.

Требования стандарта отображают таблицы Приложения 2.

Инициализация переменных.

Инициализация переменных. В соответствии с синтаксисом языка переменные автоматической памяти после определения по умолчанию имеют неопределенные значения. Надеяться на то, что они равны, например, 0, нельзя. Однако переменным можно присваивать начальные значения, явно указывая их в определениях:

тип имя_ переменной=начальное_значение;

Этот прием назван инициализацией. В отличие от присваивания, которое осуществляется в процессе выполнения программы, инициализация выполняется при выделении для переменной участка памяти. Примеры определений с инициализацией:

Именованные константы.

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

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

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

const тип имя_константы=значение_константы;

Здесь const - квалификатор типа, указывающий, что определяемый объект имеет постоянное значение, т.е. доступен только для чтения; тип - один из типов объектов; имя_константы -идентификатор; значение_константы должно соответствовать ее типу. Примеры:

В последнем определении тип константы не указан, по умолчанию ей приписывается тип int.

Третью возможность вводить именованные константы обеспечивает препроцессорная директива

#define имя_константы значение_константы

Обратите внимание на отсутствие символа "точка с запятой" в конце директивы. Подробному рассмотрению директивы #define будут посвящены два параграфа главы 3. Здесь мы только упоминаем о возможности с ее помощью определять именованные константы. Кроме того, отметим, что в конкретные реализации компиляторов с помощью директив #define включают целый набор именованных констант с фиксированными именами (см. главу 3 и Приложение 2).

Отличие определения именованной константы

от определения препроцессорной константы с таким же значением

состоит внешне в том, что в определении константы Е явно задается ее тип, а при препроцессорном определении константы EULER ее тип определяется "внешним видом" значения константы. Например, следующее определение

вводит обозначение NEXT для символьной константы 'Z'. Это соответствует такому определению:

Однако различия между обычной именованной константой и препроцессорной константой, вводимой директивой #define, гораздо глубже и принципиальнее. До начала компиляции текст программы на языке Си обрабатывается специальным компонентом транслятора - препроцессором. Если в тексте встречается директива

а ниже ее в тексте используется имя константы EULER, например, в таком виде:

то препроцессор заменит каждое обозначение EULER на ее значение и сформирует такой текст:

Далее текст от препроцессора поступает к компилятору, который уже "и не вспомнит" о существовании имени EULER, использованного в препроцессорной директиве #define. Константы, определяемые на препроцессорном уровне с помощью директивы #define, очень часто используются для задания размеров массивов, что будет продемонстрировано позже.

Итак, основное отличие констант, определяемых препроцессорными директивами #define, состоит в том, что эти константы вводятся в текст программы до этапа ее компиляции. Специальный компонент транслятора - препроцессор обрабатывает исходный текст программы, подготовленный программистом, и делает в этом тексте замены и подстановки. Пусть в исходном тексте встречается директива:

Это означает, что каждое последующее использование в тексте программы имени ZERO будет заменяться на 0.0.

Рис. 1.1 иллюстрирует некоторые принципы работы препроцессора. Его основное отличие от других компонентов транслятора - обработка программы выполняется только на уровне ее текста. На входе препроцессора - текст с препроцессорными директивами, на выходе препроцессора - модифицированный текст без препроцессорных директив. Этот выходной модифицированный текст изменен по сравнению с входным текстом за счет выполнения препроцессорных директив, но сами препроцессорные директивы в выходном тексте отсутствуют. Полностью все препроцессорные директивы будут рассмотрены позже в главе 3. В связи с именованными константами здесь рассматривается только одна из возможностей директивы #define -простая подстановка.

Текст до препроцессора (исходный текст программы):

Текст после процессора:

Рис. 1.1. Обработка текста программы препроцессором

Имена PI и ZERO (см. рис. 1.1) после работы препроцессора заменены в тексте программы на определенные в двух директивах #define значения (3.141593 и 0.0).

Обратите внимание, что подстановка не выполняется в комментариях и в строковых константах. В примере на рис. 1.1 идентификатор ZERO остался без изменений в комментарии (/* Сравнение с константой ZERO */).

Именно с помощью набора именованных препроцессорных констант стандарт языка Си рекомендует авторам компиляторов определять предельные значения всех основных типов данных. Для этого в языке определен набор фиксированных имен, каждое из которых является именем одной из констант, определяющих то или иное предельное значение. Например:

FLT_MAX - максимальное число с плавающей точкой типа float;

CHAR_BIT - количество битов в байте;

INT_MIN - минимальное значение для данных типа int.

Общий список стандартных препроцессорных именованных констант для арифметических данных дан в Приложении 2.

Чтобы использовать в программе указанные именованные препроцессорные константы, недостаточно записать их имена в программе. Предварительно в текст программы необходимо включить препроцессорную директиву такого вида:

#include <имя_заголовочного_файла>

где в качестве имени_заголовочного_файла подставляются:

limits.h - для данных целых типов;

float.h - для вещественных данных.

В заголовочный файл limits.h авторы компилятора поместили набор препроцессорных директив, среди которых есть такие (приведены значения в шестнадцатеричном виде для Turbo С):

В заголовочном файле float.h находятся директивы, определяющие константы, связанные с представлением данных вещественных типов. Например:

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

Подробно о возможностях препроцессорных средств будет говориться в главе 3. Сейчас достаточно знать, что, записав в тексте своей программы директиву

#include <limits.h>

можно использовать в программе стандартные именованные константы CHAR_BIT, SHRTJMIN и т.д. (см. Приложение 2), а уж их значениями будут те числа, которые включили в директивы #define авторы конкретного компилятора и конкретной библиотеки.

Если включить в программу директиву

#include <float.h>

то станут доступными именованные константы предельных значений числовых данных вещественных типов (см. Приложение 2).

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