Главная страница
Навигация по странице:

  • Переменные и именованные константы Переменная как объект.

  • Определение переменных.

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

  • Таблица 1.3. Основные типы данных

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

  • Рис.

  • Курс на Си. Подбельский. Курс программирования на Си. В., Фомин С. С. Курс программирования на языке Си Учебник


    Скачать 1.57 Mb.
    НазваниеВ., Фомин С. С. Курс программирования на языке Си Учебник
    АнкорКурс на Си
    Дата18.02.2023
    Размер1.57 Mb.
    Формат файлаdocx
    Имя файлаПодбельский. Курс программирования на Си.docx
    ТипУчебник
    #943863
    страница4 из 42
    1   2   3   4   5   6   7   8   9   ...   42

    Строки, или строковые константы. Формально строки (в со­ответствии со стандартом) не относятся к константам языка Си, а представляют собой отдельный тип его лексем. Для них в лите­ратуре используется еще одно название - «строковые литералы». Строковая константа определяется как последовательность симво­лов (см. выше символьные константы), заключенная в двойные ка­вычки (не в апострофы):

    "Образец строки"

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

    "\n Текст \n разместится \n в 3-х строках дисплея"

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

    Таким образом, количество байтов, выделяемое в памяти ЭВМ для представления значения строки, ровно на 1 больше, чем число символов в записи этой строковой константы:

    "Эта строка занимает в памяти ЭВМ 43 байта."

    "Строка в 18 байт."

    При работе с символьной информацией нужно помнить, что дли­на константы 'F' равна 1 байту, а длина строки "F" равна 2 байтам.

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

    Если в последовательности символов (литер) константы встреча­ется литера '\', за которой до признака '\n' конца строки текстового файла размещены только пробелы, то эти пробелы вместе с сим­волом '\' и окончанием '\n' удаляются, и продолжением строковой константы считается следующая строка текста. Например, следую­щий текст представляет одну строковую константу:

    "Шалтай-Болтай \ сидел на стене."

    В программе эта константа будет эквивалентна такой:

    "Шалтай-Болтай сидел на стене."

    Начальные (левые) пробелы в продолжении константы на новой строке не удаляются, а считаются входящими в строковую конс­танту.

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

    "Шалтай-Болтай" " свалился во сне."

    Воспринимается как одна константа:

    "Шалтай-Болтай свалился во сне."

    Тем же правилам подчиняются и строковые константы, разме­щенные на разных строках. Как одна строка будет воспринята по­следовательность

    "Вся королевская "

    "конница,

    "вся королевская "

    "рать"

    Эти четыре строковые константы эквивалентны одной:

    "Вся королевская конница, вся королевская рать"

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

      1. Переменные и именованные

    константы

    Переменная как объект. Одним из основных понятий языка Си является объект - именованная область памяти. Частный случай объекта - переменная. Отличительная особенность переменной со­стоит в возможности связывать с ее именем различные значения, совокупность которых определяется типом переменной. При зада­нии значения переменной в соответствующую ей область памяти помещается код этого значения. Доступ к значению переменной наи­более естественно обеспечивает ее имя, а доступ к участку памяти возможен только по его адресу. О взаимосвязях имен и адресов бу­дет подробно говориться в главе, посвященной указателям и работе с памятью ЭВМ. Для целей первых глав будет вполне достаточно интерпретировать понятие переменной как пару «имя - значение».

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

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

    Простейшая форма определения переменных:

    тип список_имен_переменных,

    где имена переменных - это выбранные программистом идентифика­торы, которые в списке разделяются запятыми; тип - один из уже упоминаемых (в связи с константами) типов.

    Определены целочисленные типы (перечислены в порядке не­убывания длины внутреннего представления):

    • char - целый длиной не менее 8 бит;

    • short int - короткий целый (допустима аббревиатура short);

    • int - целый;

    • long - длинный целый.

    Каждый из целочисленных типов может быть определен либо как знаковый signed, либо как беззнаковый unsigned (по умолча­нию signed).

    Различие между этими двумя типами - в правилах интерпре­тации старшего бита внутреннего представления. Спецификатор signed требует, чтобы старший бит внутреннего представления воспринимался как знаковый; unsigned означает, что старший бит внутреннего представления входит в код представляемого числово­го значения, которое считается в этом случае беззнаковым. Выбор знакового или беззнакового представления определяет предельные значения, которые можно представить с помощью описанной пере­менной. Например, на современном ПК переменная типа unsigned int позволяет представить числа от 0 до 65535, а переменной типа signed int (или просто int) соответствуют значения в диапазоне от -32768 до +32767. Чтобы глубже понять различие между целой ве­личиной и целой величиной без знака, следует обратить внимание на результат выполнения унарной операции "-" (минус) над целой величиной и целой величиной без знака. Для целой величины ре­зультат очевиден и тривиален. Результатом при использовании це­лой величины без знака является

    2n - (значение_величины_без_знака),

    где n - количество разрядов, отведенное для представления вели­чины без знака.

    По умолчанию при отсутствии в качестве префикса ключевого слова unsigned любой целый тип считается знаковым (signed). Та­ким образом, употребление совместно со служебными словами char, short, int, long префикса signed излишне. Допустимо отдельное ис­пользование обозначений (спецификаторов) «знаковости». При этом

    signed эквивалентно signed int;

    unsigned эквивалентно unsigned int.

    Примеры определений целочисленных переменных:

    char symbol, cc;

    unsigned char code;

    int number, row;

    unsigned long long_number;

    Обратите внимание на необходимость символа «точка с запятой» в конце каждого определения.

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

    • float - вещественный одинарной точности;

    • double - вещественный удвоенной точности;

    • long double - вещественный максимальной точности.

    Значения всех вещественных типов в ЭВМ представляются с «плавающей точкой», то есть с мантиссой и порядком, как было рассмотрено при определении констант (§1.2). Примеры определе­ний вещественных переменных:

    float x, X, cc3, pot_8; double e, Stop, B4;

    Предельные значения переменных. Предельные значения кон­стант (и соответствующих переменных) разработчики компиляторов вправе выбирать самостоятельно, исходя из аппаратных возможно­стей компьютера. Однако при такой свободе выбора стандарт языка требует, чтобы для значений типа short и int было отведено не менее 16 бит, для long - не менее 32 бит. При этом размер long должен быть не менее размера int, а int - не менее short. Предельные значения арифметических констант и переменных для большинства компиля­торов, реализованных на современных ПК, приведены в табл. 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

    long long

    64

    -9223372036854775808 ...

    9223372036854775807

    float

    32

    3.4E-38 ... 3.4E+38

    double

    64

    1.7E-308 ... 1.7E+308

    long double

    80

    3.4E-4932 ... 1.1E+4932

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

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

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

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

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

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

    float pi=3.1415, cc=1.23;

    unsigned int year=1997;

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

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

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

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

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

    const double E=2.718282;

    const long M=99999999;

    const F=765;

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

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

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

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

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

    const double E=2.718282;

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

    #define EULER 2.718282

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

    #define NEXT 'Z'

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

    const char NEXT = 'Z';

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

    #define EULER 2.718282

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

    double mix = EULER; d = alfa*EULER;

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

    double mix = 2.718282;

    d = alfa*2.718282;

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

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

    #define ZERO 0.0

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

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

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

    //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. Обработка текста программы
    препроцессором


    тантами здесь рассматривается только одна из возможностей дирек­тивы #define - простая подстановка.

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

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

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


    написать администратору сайта