Языки программирования
C и C++
Содержание
Язык программирования Си
Введение ..........................................................................................................................................................3
Множества символов ....................................................................................................................................4
Константы ....................................................................................................................................................10
Идентификаторы .........................................................................................................................................14
Ключевые слова ...........................................................................................................................................15
Комментарии ...............................................................................................................................................16
Лексемы ........................................................................................................................................................17
Исходная программа ...................................................................................................................................18
Исходные файлы .........................................................................................................................................19
Выполнение программ ................................................................................................................................21
«Время жизни» и «Видимость» ....................................................................................................................22
Классы имен ................................................................................................................................................24
Объявления ...................................................................................................................................................27
Спецификаторы типов ................................................................................................................................28
Область значений величин ..........................................................................................................................29
Деклараторы .................................................................................................................................................30
Объявления переменной ..............................................................................................................................34
Классы памяти .............................................................................................................................................45
Инициализация ...........................................................................................................................................51
Объявления типов .......................................................................................................................................54
Имена типов .................................................................................................................................................55
Выражения и присваивания .........................................................................................................................56
Операнды .....................................................................................................................................................57
Операции ......................................................................................................................................................63
Операции присваивания ..............................................................................................................................72
Старшинство и порядок выполнения .........................................................................................................75
Побочные эффекты .....................................................................................................................................75
Преобразования типов ................................................................................................................................76
Операторы .....................................................................................................................................................80
Функции ........................................................................................................................................................90
Язык программирования С++
Введение ........................................................................................................................................................98
Лексика .......................................................................................................................................................101
Содержание
643
Синтаксис ...................................................................................................................................................106
Область видимости .....................................................................................................................................106
Определения ...............................................................................................................................................108
Компоновка ................................................................................................................................................108
Классы памяти ............................................................................................................................................108
Основные типы ...........................................................................................................................................109
Производные типы .....................................................................................................................................110
Объекты и LVALUE (адреса) .....................................................................................................................110
Символы и целые ........................................................................................................................................111
Преобразования ..........................................................................................................................................112
Выражения и операции ...............................................................................................................................114
Описания ....................................................................................................................................................127
Спецификаторы класса памяти ..................................................................................................................128
Описатели ....................................................................................................................................................131
Описания классов .......................................................................................................................................137
Инициализация ..........................................................................................................................................150
Перегруженные имена функций .................................................................................................................157
Описание перечисления ..............................................................................................................................158
Описание Asм ..............................................................................................................................................159
Операторы ..................................................................................................................................................159
Внешние определения ...............................................................................................................................165
Командные строки компилятора ...............................................................................................................167
Обзор типов ................................................................................................................................................170
Соображения мобильности ........................................................................................................................174
Свободная память ......................................................................................................................................175
Справочник по работе с DOS
Управление памятью в DOS .......................................................................................................................177
Модели памяти ..........................................................................................................................................182
Программирование со смешанными моделями и модификаторы адресации ........................................185
Оверлеи (VROOMM) ..................................................................................................................................192
Математические операции .........................................................................................................................202
Видео_функции ..........................................................................................................................................211
Библиотеки DOS ........................................................................................................................................236
Отладчик Turbo Debugger
Назначение отладчика ................................................................................................................................245
Установка и настройка Turbo Debugger .....................................................................................................246
Выполнение программ с отладчиком ........................................................................................................255
Интерфейс отладчика ................................................................................................................................269
Содержание
644
Специальные средства Turbo Debugger .....................................................................................................274
Точки останова ..........................................................................................................................................281
Окно Log .....................................................................................................................................................291
Окно Watches ..............................................................................................................................................293
Окно Variables ............................................................................................................................................295
Окна Inspector ............................................................................................................................................297
Окно Stack ..................................................................................................................................................299
Вычисление выражений .............................................................................................................................301
Отладка на уровне ассемблера ...................................................................................................................308
Отладка в Windows .....................................................................................................................................318
Трассировка исключительных ситуаций операционной системы ..........................................................325
Отладка объектно_ориентированных программ .......................................................................................327
Отладка резидентных программ и драйверов устройств ..........................................................................331
Турбо Си ++
Интегрированная среда разработки ...........................................................................................................342
Строка меню и меню ..................................................................................................................................342
Окна TURBO C++ .....................................................................................................................................343
Работа с экранным меню ............................................................................................................................344
Структура файла, типы данных и операторов ввода_вывода ....................................................................351
Арифметические, логические операции и операции отношения и присваивания .................................355
Логическая организация программы и простейшее использованиефункций ........................................359
Логическая организация простой программы ...........................................................................................360
Использование констант различных типов ...............................................................................................360
Управляющие структуры .............................................................................................................................362
Приемы объявления и обращения к массивам, использование функций
и директивы define при работе с массивами ..............................................................................................364
Трюки программирования
Правило «право_лево» ................................................................................................................................367
STLport 4.0 .................................................................................................................................................368
Новый язык программирования от Microsoft: C# ...................................................................................370
C++ Builder ...............................................................................................................................................372
Применение «умных» указателей .............................................................................................................376
Рассуждения на тему «Умных» указателей .................................................................................................382
Виртуальные деструкторы .........................................................................................................................389
Запись структур данных в двоичные файлы ............................................................................................392
Оператор безусловного перехода goto ......................................................................................................399
Виртуальный конструктор ........................................................................................................................403
Чтение исходных текстов ............................................................................................................................410
Содержание
645
Функция gets() ...........................................................................................................................................412
Свойства ....................................................................................................................................................414
Комментарии ............................................................................................................................................418
Веб_программирование ...............................................................................................................................424
Ошибки работы с памятью .........................................................................................................................429
Создание графиков с помощью ploticus ...................................................................................................432
Автоматизация и моторизация приложения ..............................................................................................435
Обзор C/C++ компиляторов EMX и Watcom ...........................................................................................456
Использование директивы #import ............................................................................................................462
Создание системных ловушек Windows на Borland C++ Builder 5 ...........................................................480
Тонкости и хитрости в вопросах и ответах .....................................................................................................
Приложения
Средства для разработчиков .......................................................................................................................635
Список использованной литературы .........................................................................................................640
Содержание
646
Язык программирования Си
Введение
Си — это язык программирования общего назначения,
хорошо известный своей эффективностью, экономичностью, и
переносимостью. Указанные преимущества Си обеспечивают
хорошее качество разработки почти любого вида программного
продукта. Использование Си в качестве инструментального языка
позволяет получать быстрые и компактные программы. Во
многих случаях программы, написанные на Си, сравнимы по
скорости с программами, написанными на языке ассемблера.
При этом они имеют лучшую наглядность и их более просто
сопровождать.
Си сочетает эффективность и мощность в относительно
малом по размеру языке. Хотя Си не содержит встроенных
компонент языка, выполняющих ввод_вывод, распределение
памяти, манипуляций с экраном или управление процессами, тем
не менее, системное окружение Си располагает библиотекой
объектных модулей, в которой реализованы подобные функции.
Библиотека поддерживает многие из функций, которые
требуются.
Это решение позволяет изолировать языковые особенности
от специфики процессора, на котором выполняется
результирующая программа. Строгое определение языка делает
его независимым от любых деталей операционной системы или
машины. В то же время программисты могут добавить в
библиотеку специфические системные программы, чтобы более
эффективно использовать конкретной особенности машины.
Перечислим некоторые существенные особенности
языка Си:
? Си обеспечивает полный набор операторов структурного
программирования.
? Си предлагает необычно большой набор операций.
Многие операции Си соответствуют машинным
командам и поэтому допускают прямую трансляцию
Язык программирования Си
3
в машинный код. Разнообразие операций позволяет
выбирать их различные наборы для минимизации
результирующего кода.
? Си поддерживает указатели на переменные и функции.
Указатель на объект программы соответствует
машинному адресу этого объекта.
Посредством разумного использования указателей можно
создавать эффективно_выполняемые программы, так как
указатели позволяют ссылаться на объекты тем же самым путем,
как это делает машина. Си поддерживает арифметику указателей,
и тем самым позволяет осуществлять непосредственный доступ и
манипуляции с адресами памяти.
В своем составе Си содержит препроцессор, который
обрабатывает текстовые файлы перед компиляцией. Среди его
наиболее полезных приложений при написании программ на Си
являются: определение программных констант, замена вызовов
функций аналогичными, но более быстрыми макросами,
условная компиляция. Препроцессор не ограничен
процессированием только исходных текстовых файлов Си, он
может быть использован для любого текстового файла.
Си — гибкий язык, позволяющий принимать в конкретных
ситуациях самые разные решения. Тем не менее, Си налагает
незначительные ограничения в таких, например, действиях, как
преобразование типов. Во многих случаях это является
достоинством, однако программисты должны хорошо знать язык,
чтобы понимать, как будут выполняться их программы.
Множества символов
В программах на Си используется два множества символов:
множество символов Си и множество представимых символов.
Множество символов Си содержит буквы, цифры и знаки
пунктуации, которые имеют определенное значение для
компилятора Си. Программы на Си строятся путем
комбинирования в осмысленные конструкции символов из
множества Си.
Множество символов Си является подмножеством
множества представимых символов. Множество представимых
символов состоит из всех букв, цифр и символов, которые
Язык программирования Си
4
пользователь может представить графически как отдельный
символ. Мощность множества представимых символов зависит от
типа терминала, который используется.
Программа на Си может содержать только символы из
множества символов Си, за исключением строковых литералов,
символьных констант и комментариев, где может быть
использован любой представимый символ. Каждый символ из
множества символов Си имеет вполне определенный смысл для
компилятора Си. Компилятор выдает сообщение об ошибке,
когда он встречает неверно использованные символы или
символы, не принадлежащие множеству Си.
Буквы и цифры
Множество символов Си включает большие и малые буквы
из английского алфавита и 10 десятичных арабских цифр:
? большие английские буквы:
A B C D E F G H I J K L M N O P Q R T U V W X Y Z
? малые английские буквы:
a b c d e f g h i j k l m n o p q r t u v w x y z
? десятичные цифры:
0 1 2 3 4 5 6 7 8 9
Буквы и цифры используются при формировании констант,
идентификаторов и ключевых слов. Все эти конструкции
описаны ниже.
Компилятор Си рассматривает одну и ту же малую и
большую буквы как отличные символы. Если в данной записи
использованы малые буквы, то замена малой буквы a на большую
букву A сделает отличной данную запись от предшествующей.
Пробельные символы
Пробел, табуляция, перевод строки, возврат каретки, новая
страница, вертикальная табуляция и новая строка — это символы,
называемые пробельными, поскольку они имеют то же самое
назначение, как и пробелы между словами и строками на
печатной странице. Эти символы разделяют объекты,
определенные пользователем, такие, как константы и
идентификаторы, от других объектов программы.
Язык программирования Си
5
Символ CONTROL_Z рассматривается как индикатор конца
файла. Компилятор игнорирует любой текст, следующий за
символом CONTROL_Z.
Компилятор Си игнорирует пробельные символы, если они
не используются как разделители или как компоненты
константы_символа или строковых литералов. Это нужно иметь в
виду, чтобы дополнительно использовать пробельные символы
для повышения наглядности программы (например, для
просмотра редактором текстов).
Знаки пунктуации и специальные символы
Знаки пунктуации и специальные символы из множества
символов Си используются для различных целей, от организации
текста программы до определения заданий, которые будут
выполнены компилятором или откомпилированной программой.
Эти символы имеют специальный смысл для компилятора
Си. Их использование в языке Си описывается в дальнейшем
содержании руководства. Знаки пунктуации из множества
представимых символов, которые не представлены в данном
списке, могут быть использованы только в строковых литералах,
константах_символах и комментариях.
ESC!последовательности
ESC_последовательности — это специальные символьные
комбинации, которые представляют пробельные символы и не
графические символы в строках и символьных константах.
Их типичное использование связано со спецификацией
таких действий, как возврат каретки и табуляция, а также для
задания литеральных представлений символов, таких как символ
двойная кавычка. ESC_последовательность состоит из наклонной
черты влево, за которой следует буква, знаки пунктуации, «'», «"»,
«\» или комбинация цифр.
Если наклонная черта влево предшествует символу, не
включенному в этот список, то наклонная черта влево
игнорируется, а символ представляется как литеральный.
Например, изображение \c представляет символ "c" в литеральной
строке или константе_символе.
Последовательности \ddd и \xdd позволяют задать любой
символ в ASCII (Американский стандартный код
Язык программирования Си
6
информационного интерфейса) как последовательность трех
восьмеричных цифр или двух шестнадцатеричных цифр.
Например, символ пробела может быть задан как \010 или \x08.
Код ASCII нуль может быть задан как \0 или \x0. В восьмеричной
ESC_последовательности могут быть использованы от одной до
трех восьмеричных цифр. Например, символ пробела может быть
задан как \10. Точно так же в шестнадцатеричной
ESC_последовательности могут быть использованы от одной до
двух шестнадцатеричных цифр. Так, шестнадцатеричная
последовательность для символа пробела может быть задана как
\x08 или \x8.
Важно: Когда используется восьмеричная или
шестнадцатеричная ESC_последовательность в строках, то
нужно полностью задавать все цифры ESC_последовательности
(три цифры для восьмеричной и две цифры для
шестнадцатеричной ESC_ последовательностей).
Иначе, если символ непосредственно следующий за
ESC_последовательностью, случайно окажется восьмеричной или
шестнадцатеричной цифрой, то он проинтерпретируется как
часть последовательности. Например, строка \x7Bell при выводе
на печать будет выглядеть как ell, поскольку \x7B
проинтерпретируется как символ левой фигурной скобки ().
Строка \x07Bell будет правильным представлением символа
звонок с последующим словом Bell.
ESC_последовательности позволяют посылать
неграфические управляющие символы к внешним устройствам.
Например, ESC_последовательность \033 часто используется как
первый символ команд управления терминалом и принтером.
Неграфические символы всегда должны представляться
ESC_последовательностями, поскольку, непосредственное
использование в программах на Си неграфических символов
будет иметь непредсказуемый результат.
Наклонная черта влево \ помимо определения
ESC_последовательностей используется также, как символ
продолжения строки в препроцессорных определениях.
Если символ новая строка следует за наклонной чертой
влево, то новая строка игнорируется и следующая строка
рассматривается, как часть предыдущей строки.
Язык программирования Си
7
Операции
Операции — это специальные комбинации символов,
специфицирующие действия по преобразованию различных
величин. Компилятор интерпретирует каждую из этих
комбинаций как самостоятельную единицу, называемую
лексемой token.
Ниже представлен список операций. Операции должны
использоваться точно так, как они тут представлены: без
пробельных символов между символами в тех операциях, которые
представлены несколькими символами.
Операция sizeof не включена в этот список, так как она
представляет собой ключевое слово, а не символ.
!
Логическое НЕ
~
Побитовое дополнение
+
Сложение
!
Вычитание, арифметическое отрицание
*
Умножение
/
Деление
%
Остаток
<<
Сдвиг влево
>>
Сдвиг вправо
<
Меньше
<=
Меньше или равно
Язык программирования Си
8
>
Больше
>=
Больше или равно
==
Равно
!=
Не равно
&
Побитовое И, адрес от
|
Побитовое включающее ИЛИ
^
Побитовое исключающее ИЛИ
&&
Логическое И
||
Логическое ИЛИ
'
Последовательное выполнение (запятая)
?:
Операция условного выражения
++
Инкремент
!!
Декремент
=
Простое присваивание
+=
Сложение с присваиванием
!=
Вычитание с присваиванием
Язык программирования Си
9
*=
Умножение с присваиванием
/=
Деление с присваиванием
%=
Остаток с присваиванием
>>=
Сдвиг вправо с присваиванием
<<=
Сдвиг влево с присваиванием
&=
Побитовое И с присваиванием
|=
Побитовое включающее ИЛИ с присваиванием
^=
Побитовое исключающее ИЛИ с присваиванием
Важно: Операция условного выражения ?: является
тернарной, а не двухсимвольной операцией. Формат условного
выражения следующий:
Константы
Константа — это число, символ или строка символов.
Константы используются в программе как неизменяемые
величины. В языке Си различают четыре типа констант: целые
константы, константы с плавающей точкой, константы_символы
и строчные литералы.
Целые константы
Целая константа — это десятичное, восьмеричное или
шестнадцатеричное число, которое представляет целую величину.
Десятичная константа имеет следующий формат представления:
где
Язык программирования Си
10
Восьмеричная константа имеет следующий формат
представления:
0
где
Запись ведущего нуля необходима.
Шестнадцатеричная константа имеет один из следующих
форматов представления:
0x
0X
где
Шестнадцатеричная цифра может быть цифрой от 0 до 9 или
буквой (большой или малой) от A до F. В представлении
константы допускается «смесь» больших и малых букв. Запись
ведущего нуля и следующего за ним символа x или X необходима.
Пробельные символы не допускаются между цифрами целой
константы. Ниже иллюстрируются примеры целых констант.
10 012 0xa или 0xA
132 0204 0x84
32179 076663 0x7dB3 или 0x7DB3
Целые константы всегда специфицируют положительные
величины. Если требуется отрицательные величины, то
необходимо сформировать константное выражение из знака
минус и следующей за ним константы. Знак минус
рассматривается как арифметическая операция.
Каждая целая константа специфицируется типом,
определяющим ее представление в памяти и область значений.
Десятичные константы могут быть типа int или long.
Восьмеричные и шестнадцатеричные константы в
зависимости от размера могут быть типа int, unsigned int, long или
unsigned long. Если константа может быть представлена как int,
она специфицируется типом int. Если ее величина больше, чем
максимальная положительная величина, которая может быть
представлена типом int, но меньше величины, которая
представляется в том же самом числе бит как и int, она задается
типом unsigned int. Наконец, константа, величина которой больше
чем максимальная величина, представляемая типом unsigned int,
задается типом long или unsigned long, если это необходимо.
Язык программирования Си
11
Важность рассмотренных выше правил состоит в том, что
восьмеричные и шестнадцатеричные константы не содержат
«знаковых» расширений, когда они преобразуются к более
длинным типам.
Программист может определить для любой целой константы
тип long, приписав букву l или L в конец константы.
Константы с плавающей точкой
Константа с плавающей точкой — это действительное
десятичное положительное число. Величина действительного
числа включает целую, дробную части и зкспоненту. Константы с
плавающей точкой имеют следующий формат представления:
[
где
или e — символ экспоненты. Целая или дробная части константы
могут быть опушены, но не обе сразу. Десятичная точка может
быть опущена толькотогда, когда задана экспонента.
Экспонента состоит из символа экспоненты, за которым
следует целочисленная величина экспоненты, возможно
отрицательная.
Пробельные символы не могут разделять цифры или
символы константы.
Константы с плавающей точкой всегда специфицируют
положительные величины. Если требуются отрицательные
величины, то необходимо сформировать константное выражение
из знака минус и следующей за ним константы. Знак минус
рассматривается как арифметическая операция.
Примеры констант с плавающей точкой и константных
выражений:
15.75
1.575E1
1575e_2
_0.0025
_2.5e_3
25e_4
Целая часть константы с плавающей точкой может быть
опущена, например:
.75
Язык программирования Си
12
.0075e2
_.125
_.175E_2
Все константы с плавающей точкой имеют тип double.
Константа!символ
Константа_символ — это буква, цифра, знак пунктуации или
ESC_символ, заключенные в одиночные кавычки. Величина
константы_символа равна значению представляющего ее кода
символа.
Константа_символ имеет следующую форму представления:
"
где
представимых символов, включая любой ESC_символ, исключая
одиночную кавычку ('), наклонную черту влево (\) и символ
новой строки.
Чтобы использовать одиночную кавычку или наклонную
черту влево в качестве константы_символа, необходимо вставить
перед этими знаками наклонную черту влево. Чтобы представить
символ новой строки, необходимо использовать запись \n.
Строковые литералы
Строковый литерал — это последовательность букв, цифр и
символов, заключенная в двойные кавычки. Строковый литерал
рассматривается как массив символов, каждый элемент которого
представляет отдельный символ. Строковый литерал имеет
следующую форму представления:
"
где
представимых символов, исключая двойную кавычку, наклонную
черту влево и символ новой строки. Чтобы использовать символ
новой строки в строковом литерале, необходимо напечатать
наклонную черту влево, а затем символ новой строки.
Наклонная черта влево вместе с символом новой строки
будут проигнорированы компилятором, что позволяет
формировать строковые литералы, располагаемые более чемв
одной строке.
Язык программирования Си
13
Например, строковый литерал:
"Long strings can be bro\
cken into two pieces."
идентичен строке:
Long strings can be brocken into two pieces.
Чтобы использовать двойные кавычки или наклонную черту
влево внутри строкового литерала, нужно представить их с
предшествующей наклонной чертой влево, как показано в
следующем примере:
"This is a string literal"
"First \\ Second"
"\"Yes, I do,\" she said."
"The following line shows a null string:"
""
Заметим, что ESC_символы (такие как \\ и \") могут
появляться в строковых литералах. Каждый ESC_символ
считается одним отдельным символом.
Символы строки запоминаются в отдельных байтах памяти.
Символ null или \0 является отметкой конца строки. Каждая
строка в программе рассматривается как отдельный объект. Если
в программе содержатся две идентичные строки, то каждая из них
будет храниться в отдельном месте памяти.
Строчные литералы имеют тип char[]. Под этим
подразумевается, что строка — это массив, элементы которого
имеют тип char. Число элементов в массиве равно числу символов
в строчном литерале плюс один, поскольку символ null (отметка
конца строки) тоже считается элементом массива.
Идентификаторы
Идентификаторы — это имена переменных, функций и
меток, используемых в программе. Идентификатор создается
объявлением соответствующей ему переменной или функции.
После этого его можно использовать в последующих операторах
программы. Идентификатор — это последовательность из одной
или более букв, цифр или подчерков (_), которая начинается с
буквы или подчерка. Допускается любое число символов в
идентификаторе, однако только первые 31 символ распознаются
компилятором. (Программы, использующие результат работы
Язык программирования Си
14
компилятора, такие как, линкер, могут распознавать меньшее
число символов).
При использовании подчерков в идентификаторе нужно
быть осторожным, поскольку идентификаторы, начинающиеся с
подчерка могут совпадать (войти в конфликт) с именами
«скрытых» системных программ.
Примеры идентификаторов:
temp1
toofpage
skip12
Компилятор Си рассматривает буквы верхнего и нижнего
регистров как различные символы. Поэтому можно создать
отдельные независимые идентификаторы, которые совпадают
орфографически, но различаются большими и малыми буквами.
Например, каждый из следующихидентификаторов является
уникальным:
add
ADD
Add aDD
Компилятор Си не допускает идентификаторов, которые
имеют ту же самую орфографию, что и ключевые слова.
Важно: По сравнению с компилятором, сборщик может в
большей степени ограничивать количество и тип символов для
глобальных идентификаторов, и в отличие от компилятора не
делать различия между большими и малыми буквами.
Ключевые слова
Ключевые слова — это предопределенные идентификаторы,
которые имеют специальное значение для компилятора Си. Их
можно использовать только так как они определены. Имена
объектов программы не могут совпадать с названиями ключевых
слов.
Список ключевых слов:
auto double int struct
break else long switch
case enum register typedef
char extern return union
Язык программирования Си
15
const float short unsigned
continue for signed void
default goto sizeof while
do if static volatile
Ключевые слова не могут быть переопределены. Тем не
менее, они могут быть названы другим текстом, но тогда перед
компиляцией они должны быть заменены посредством
препроцессора на соответствующие ключевые слова.
Ключевые слова const и volatile зарезервированы для
будущего использования.
Следующие идентификаторы могут быть ключевыми
словами для некоторых приложений:
cdecl
far
fortran
huge
near
pascal
Комментарии
Комментарий — это последовательность символов, которая
воспринимается компилятором как отдельный пробельный
символ или, другими словами, игнорируется.
Комментарий имеет следующую форму представления:
/*
где
множества представимых символов, включая символы новой
строки, но исключая комбинацию */.
Это означает, что комментарии могут занимать более одной
строки, но не могут быть вложенными.
Комментарии допускаются везде, где разрешены
пробельные символы. Компилятор игнорирует символы
комментария, в частности, в комментариях допускается запись
ключевых слов и это не приведет к ошибке. Так как компилятор
рассматривает комментарий как символ пробела, то комментарии
не могут появляться внутри лексем.
Язык программирования Си
16
Следующие примеры иллюстрируют некоторые
комментарии:
/* Comments can separate and document
lines of a program. */
/* Comments can contain keywords such as for
and while */
/*******************************************
Comments can occupy several lines.
*******************************************/
Так как комментарии не могут содержать вложенных
комментариев, то следующий пример будет ошибочным:
/* You cannot/* nest */ comments */
Компилятор распознает первую комбинацию */ после слова
nest как конец комментария. Затем, компилятор попытается
обрабатывать оставшийся текст и выработает сообщение об
ошибке. Чтобы обойти компиляцию комментариев больших
размеров, нужно использовать директиву #if препроцессора.
Лексемы
Когда компилятор обрабатывает программу, он разбивает
программу на группы символов, называемых лексемами.
Лексема — это единица текста программы, которая имеет
определенный смысл для компилятора и которая не может быть
разбита в дальнейшем. Операции, константы, идентификаторы и
ключевые слова, описанные в этом разделе, являются примерами
лексем. Знаки пунктуации, такие как квадратные скобки,
фигурные скобки, угловые скобки, круглые скобки и запятые,
также являются лексемами. Границы лексем определяются
пробельными символами и другими лексемами, такими как
операции и знаки пунктуации. Чтобы предупредить
неправильную работу компилятора, запрещаются пробельные
символы между символами идентификаторов, операциями,
состоящими из нескольких символов и символами ключевых
слов.
Когда компилятор выделяет отдельную лексему, он
последовательно объединяет столько символов, сколько
возможно, прежде чем перейти к обработке следующей лексемы.
Поэтому лексемы, не разделенные пробельными символами,
могут быть проинтерпретированы неверно.
Язык программирования Си
17
Например, рассмотрим следующее выражение:
i+++j
В этом примере компилятор вначале создает из трех знаков
плюс самую длинную из возможных операций ++, а затем
обработает оставшийся знак +, как операцию сложения +.
Выражение проинтерпретируется как (i++)+(j), а не как (i)+(++j).
В таких случаях необходимо использовать пробельные символы
или круглые скобки, чтобы однозначно определить ситуацию.
Исходная программа
Исходная программа — это совокупность следующих
объектов: директив, указаний компилятору, объявлений и
определений. Директивы задают действия препроцессора по
преобразованию текста программы перед компиляцией. Указания
компилятору — это команды, выполняемые компилятором во
время процесса компиляции. Объявления задают имена и
атрибуты переменных, функций и типов, используемых в
программе. Определения — это объявления, определяющие
переменные и функции.
Определение переменной в дополнении к ее имени и типу
задает начальное значение объявленной переменной. Кроме того,
определение предполагает распределение памяти для
переменной.
Определение функции специфицирует ее структуру, которая
представляет собой смесь из объявлений и операторов, которые
образуют саму функцию. Определение функции также задает имя
функции, ее формальные параметры и тип возвращаемой
величины.
Исходная программа может содержать любое число
директив, указаний компилятору, объявлений и определений.
Любой из объектов программы имеет определенный синтаксис,
описанный в этом руководстве, и каждая составляющая может
появляться в любом порядке, хотя влияние порядка, в котором
следуют переменные и функции может быть использовано в
программе.
Нетривиальная программа всегда содержит более одного
определения функции. Функция определяет действия,
выполняемые программой.
Язык программирования Си
18
В следующем примере иллюстрируется простая исходная
программа на языке Си.
int x = 1; /* Variable definitions */
int y = 2;
extern int printf(char *,...);/* Function declaration */
main () /* Function definition
for main function */
int z; /* Variable declarations */
int w;
z = y + x; /* Executable statements */
w = y _ x;
printf("z = %d \nw = %d \n", z, x);
Эта исходная программа определяет функцию с именем main
и объявляет функцию printf. Переменные x и y задаются своими
определениями. Переменные z и w только объявляются.
Исходные файлы
Исходные программы могут быть разделены на несколько
файлов. Исходный файл Си — это текстовый файл, который
содержит часть или всю исходную программу. Он может,
например, содержать только некоторые функции, требуемые
программе. При компиляции исходной программы каждый из
исходных файлов должен быть прокомпилирован отдельно, а
затем обработан сборщиком. Отдельные исходные файлы перед
компиляцией можно соединять в один большой исходный файл
посредством директивы #include.
Исходный файл может содержать любую комбинацию
наборов: директив, указаний компилятору, объявлений и
определений. Такие объекты, как определения функций или
большие структуры данных, не могут разрываться, начинаясь в
одном файле и продолжаясь в другом.
Исходный файл не обязательно должен содержать
выполняемыеоператоры. Иногда полезно размещать описания
переменных в одном файле с тем, чтобы использовать их путем
объявления ссылок из других файлов. В этом случае определения
становятся легко доступными для поиска и модификации. Из тех
же самых соображений константы и макросы часто организуют в
отдельных #include — файлах и включают их, если требуется, в
исходные файлы.
Язык программирования Си
19
Директивы исходного файла относятся только к этому
исходному файлу и файлам, включающим его (посредством
#include). Кроме того, каждая директива относится только к части
файла, которая следует за ней. Если множество директив должно
относиться ко всей исходной программе, то все исходные файлы
должны содержать эти директивы.
Указания компилятору обычно эффективны для отдельных
областей исходного файла. Специфические действия
компилятора, задаваемые указаниями, определяются
содержанием последних.
В нижеследующем примере исходная программа состоит из
двух исходных файлов. Функции main и max представлены в
отдельных файлах. Функция main использует функцию max при
своем выполнении.
/***********************************************
Sourse file 1 _ main function
***********************************************/
#define ONE 1
#define TWO 2
#define THREE 3
extern int max(int, int); /* Function declaration */
main () /* Function definition */
int w = ONE, x = TWO, y = THREE;
int z = 0;
z = max(x,y);
w = max(z,w);
/*************************************************
Sourse file 2 _ max function
*************************************************/
int max(a,b) /* Function definition */
int a, b;
if ( a > b )
return (a);
else
return (b);
В первом исходном файле функция max объявлена без ее
определения. Это объявление известно как forward_объявление.
Определение функции main включает вызов функции max.
Язык программирования Си
20
Строки, начинающиеся с символа номер #, являются
директивами препроцессора. Директивы инструктируют
препроцессор о замене в первом исходном файле имен ONE,
TWO, THREE на соответствующие им значения. Область
действия директив не распространяется на второй исходный
файл.
Второй исходный файл содержит описание функции max.
Это определение соответствует вызовам max из первого исходного
файла.
Выполнение программ
Каждая программа содержит главную программную
функцию. В Си главная программная функция должна быть
поименована как main. Функция main служит точкой старта при
выполнении программы и обычно управляет выполнением
программы, организуя вызовы других функций. Программа
обычно завершает выполнение по окончанию функции main, хотя
она может завершиться и в других точках, в зависимостиот
окружающей обстановки.
Исходная программа обычно включает в себя несколько
функций, каждая из которых предназначена для выполнения
определенного задания. Функция main может вызывать эти
функции с тем, чтобы выполнить то или иное задание. Функция
возвращает управление при выполнении оператора return или по
окончанию самой функции (выход на конец функции).
Все функции, включая функцию main, могут быть
объявлены с параметрами. Вызываемые функции получают
значения параметров из вызывающих функций. Значения
параметров функции main могут быть переданы из внешнего
окружения.
Например, они могут быть переданы из командной строки.
Соглашение Си требует, чтобы первые два параметра
функции main назывались argc и argv.
Параметр argc определяет общее число аргументов,
передаваемых функции main. Параметр argv объявляется как
массив указателей, каждый элемент которого ссылается на
строковое представление аргумента, передаваемого функции
main. Третий параметр функции main (если он есть) традиционно
Язык программирования Си
21
задается с именем envp. Однако Си не требует этого имени.
Параметр envp — это указатель на массив указателей строковых
величин, которые определяют окружение, в котором выполняется
программа.
Операционная система поддерживает передачу значений для
argc, argv и envp параметров, а пользователь поддерживает задание
значений фактических параметров для функции main.
Соглашение о передаче параметров в большей степени
определяется операционной системой, чем самим языком Си.
Формальные параметры функции должны быть объявлены
во время описания функции.
«Время жизни» и «Видимость»
Концепции «Время жизни» и «Видимость» являются очень
важными для понимания структуры программ на Си. Время
жизни переменной может быть или глобальным или локальным.
Объект с глобальным временем жизни характеризуется
определенной памятью и значением на протяжении всей жизни
программы. Объект с локальным временем жизни захватывает
новую память при каждом входе в блок, в котором он определен
или объявлен. Когда блок завершается, локальный объект
пропадает, а следовательно пропадает его значение. Определение
блоков описывается ниже.
Объект считается видимым в блоке или исходном файле,
если известны тип и имя объекта в блоке или исходном файле.
Объект может быть глобально видимым, когда имеется в виду, что
он видим или может быть объявлен видимым на протяжении
всего исходного файла, образующего программу.
Блок — это составной оператор. Составные операторы
состоят из объявлений и операторов.
Структура функции представляет собой совокупность
составных операторов. Таким образом, функции имеют блочную
структуру, блоки, в свою очередь, могут содержать внутри себя
другие блоки.
Объявления и определения внутри блоков находятся на
внутреннем уровне. Объявления и определения вне блоков
находятся на внешнем уровне. Переменные и функции могут быть
объявлены как на внешнем уровне, так и на внутреннем.
Язык программирования Си
22
Переменные также могут быть определены на внутреннем и на
внешнем уровне, а функции определяются только на внешнем
уровне.
Все функции имеют глобальное время жизни, невзирая на
то, где они объявлены. Переменные, объявленные на внешнем
уровне, всегда имеют глобальное время жизни. Переменные,
объявленные на внутреннем уровне, всегда имеют локальное
время жизни, однако, классы памяти, специфицированные как
static и extern, могут быть использованы для объявления
глобальных переменных или ссылок на них внутри блока.
Переменные, объявленные или определенные на внешнем
уровне, видимы из точки, в которой они объявлялись или
определялись, до конца файла.
Однако, переменные, заданные классом памяти static на
внешнем уровне, видимы только внутри исходного файла, в
котором они определены.
В общем случае, переменные, объявленные или
определенные на внутреннем уровне, видимы от точки, в которой
они объявлены или определены, до конца блока, в котором
представлены объявления или определения. Эти переменные
называются локальными. Если переменная, объявленная внутри
блока, имеет то же самое имя, как и переменная, объявленная на
внешнем уровне, то определение переменной в блоке заменяет
(имеет предпочтение) определение внешнего уровня на
протяжении блока. Видимость переменной внешнего уровня
восстанавливаетсяпри завершении блока.
Блочная видимость может вкладываться. Это значит, что
блок, вложенный внутрь другого блока, может содержать
объявления, которые переопределяют переменные, объявленные
во внешнем блоке. Переопределение переменной имеет силу во
внутреннем блоке, но первоначальное определение
восстанавливается, когда управление возвращается внешнему
блоку. Переменные из внешних блоков видимы внутри всех
внутренних блоков до тех пор, пока они не переопределены во
внутреннем блоке.
Функции класса памяти static видимы только в исходном
файле, в котором они определены. Все другие функции являются
глобально видимыми.
Язык программирования Си
23
В следующем примере программы иллюстрируются блоки,
вложения и видимость переменных.
/* i defined at external level */
int i = 1;
/* main function defined at external level */
main ()
/* prints 1 (value of external level i) */
printf("%d\n", i);
/* first nested block */
/* i and j defined at internal level */
int i = 2, j = 3;
/* prints 2, 3 */
printf("%d\n%d\n", i, j);
/* second nested block */
/* i is redefined */
int i = 0;
/* prints 0, 3 */
printf("%d\n%d\n", i, j);
/* end of second nested block */
/* prints 2 (outer definition restored) */
printf("%d\n", i);
/* end of first nested block */
/* prints 1 (external level definition restored */
printf("%d\n", i);
В этом примере показано четыре уровня видимости: самый
внешний уровень и три уровня, образованных блоками.
Предполагается, что функция printf определена где_нибудь в
другом месте программы. Функция main печатает значения 1, 2, 3,
0, 3, 2, 1.
Классы имен
В любой программе на Си имена используются для ссылок
на различного рода объекты. Когда пишется программа на Си, то
в ней используются идентификаторы для именования функций,
Язык программирования Си
24
переменных, формальных параметров, областей памяти и других
объектов программы. По определенным правилам в Си
допускаются использование одного и того же идентификатора для
более чем одного программного объекта.
Чтобы различать идентификаторы для различного рода
объектов, компилятор устанавливает так называемые Классы
имен. Для избегания противоречий, имена внутри одного класса
должны быть уникальными, однако в различных классах могут
появляться идентичные имена. Это означает, что можно
использовать один и тот же идентификатор для двух или более
различных объектов, если объекты принадлежат к различным
классам имен. Однозначное разрешение ссылок компилятор
осуществляет по контексту данного идентификатора в программе.
Ниже описываются виды объектов, которые можно именовать в
программе на Си, а также правила их именования.
Имена переменныхи функций относятся к одному классу
имен вместе с формальными параметрами и перечислимыми
константами. Поэтому, имена переменных и функций этого
класса должны быть отличны от других имен с той же
видимостью.
Однако, имена переменных могут быть переопределены
внутри программных блоков.
Имена функций также могу быть переопределены в
соответствии с правилами видимости.
Имена формальных параметров функции появляются вместе
с именами переменных функции, поэтому имена формальных
параметров должны быть отличны от имен переменных.
Переобъявление формальных параметров внутри функции
приводит к ошибке.
Перечислимые константы относятся к тому же классу имен,
что и имена переменных и функций. Это означает, что имена
перечислимых констант должны быть отличны от имен всех
переменных и функций с той же самой видимостью. Однако,
подобно именам переменных, имена перечислимых констант
имеют вложенную видимость, а это означает, что они могут быть
переопределены внутри блоков.
Имена typedef относятся к одному классу имен вместе с
именами переменных и функций. Поэтому они должны быть
Язык программирования Си
25
отличны от всех имен переменных и функций с той же самой
видимостью, а также от имен формальных параметров и
перечислимых констант. Подобно именам переменных, имена,
используемые для typedef типов могут быть переопределены
внутри блоков программы.
Теги перечислений, структур и совмещений сгруппированы
вместе в одном классе имен. Каждый тег перечисления,
структуры или соединения должен быть отличен от других тегов с
той же самой видимостью. Теги не конфликтуют с любыми
другими именами.
Элементы каждой структуры или совмещения образуют
класс имен, поэтому имя элемента должно быть уникальным
внутри структуры или совмещения, но это не означает, что они
должны быть отличными от любого другого имени в программе,
включая имена элементов других структур и совмещений.
Метки операторов формируют отдельный класс имен.
Каждая метка оператора должна быть отлична от всех других
меток операторов в той же самой функции. Метки операторов
могут совпадать с именами меток других функций.
Пример:
struct student
char student[20];
int class;
int id;
student;
В этом примере имя тега структуры, элемента структуры и
переменной относятся к трем различным классам имен, поэтому
не будет противоречия между тремя объектами с именем student.
Компилятор определит по контексту программы как
интерпретировать каждый из случаев. Например, когда имя
student появится после ключевого слова struct, это будет означать,
что появился tag структуры. Когда имя student появится после
операции выбора элемента _> или ., то это означает, что имя
ссылается на элемент структуры. В другом контексте имя student
является переменной структурного типа.
Язык программирования Си
26
Объявления
Объявления Си имеют следующий синтаксис:
[
[,
где:
Спецификатор класса памяти.
Имя определяемого типа.
Идентификатор, который может быть модифицирован при
объявлении указателя, массива или функции.
Задает значение или последовательность значений,
присваиваемых переменной при объявлении.
Все переменные Си должны быть явно объявлены перед их
использованием. Функции Си могут быть объявлены явно или
неявно в случае их вызова перед определением.
Язык Си определяет стандартное множество типов данных.
К этому множеству можно добавлять новые типы данных
посредством их объявлений на типах данных уже определенных.
Объявление Си требует одного или более деклараторов.
Декларатор — это идентификатор, который может быть
определен с квадратными скобками [], звездочкой * или
круглыми скобками () для объявления массива, указателя или
функции. Когда объявляется простая переменная (такая как
символ, целое или плавающее), структура или совмещение
простых переменных, то декларатор — это идентификатор.
В Си определено четыре спецификатора класса памяти, а
именно: auto, extern, register и static.
Спецификатор класса памяти определяет, каким образом
объявляемый объект запоминается и инициализируется и из
каких частей программы можно ссылаться на него. Расположение
объявления внутри программы, а также наличие или отсутствие
других объявлений — также важные факторы при определении
видимости переменных.
Язык программирования Си
27
Спецификаторы типов
Язык Си поддерживает определения для множествабазовых
типов данных, называемых «основными» типами.
signed char float void
signed int double
signed short int
signed long int
unsigned char
unsigned int
unsignet short int
unsigned long int
Перечислимые типы также рассматриваются как основные
типы.
Типы signed char, signed int, signed short int и signed long int
вместе с соответствующими двойниками unsigned называются
типами целых.
Спецификаторы типов float и double относятся к типу
плавающих. В объявлениях переменных и функций можно
использовать любые спецификаторы целый и плавающий.
Тип void может быть использован только для объявления
функций, которые не возвращают значения.
Можно задать дополнительные спецификаторы типа путем
объявления typedef.
При записи спецификаторов типов допустимы сокращения.
В целых типах ключевое слово signed может быть опущено. Так,
если ключевое слово unsigned опускается в записи спецификатора
типа, то тип целого будет знаковым, даже если опущено ключевое
слово signed.
В некоторых реализациях могут быть использованы опции
компилятора, позволяющие изменить умолчание для типа char со
знакового на беззнаковый. Когда задана такая опция, сокращение
char имеет то же самое значение, что и unsigned char, и
следовательно ключевое слово sidned должно быть записано при
объявлении символьной величины со знаком.
Тип char используется для запоминания буквы, цифры или
символа из множества представимых символов. Значением
объекта типа char является ASCII код, соответствующий данному
Язык программирования Си
28
символу. Так как тип char интерпретируется как однобайтовая
целая величина с областью значений от _128 до 127, то только
величины от 0 до 127 имеют символьные эквиваленты.
Аналогично, тип unsigned char может запоминать величины с
областью значений от 0 до 255.
Заметим, что представление в памяти и область значений
для типов int и unsigned int не определены в языке Си. По
умолчанию размер int (со знаком и без знака) соответствует
реальному размеру целого на данной машине. Например, на 16_ти
разрядной машине тип int всегда 16 разрядов или 2 байта. На
32_х разрядной машине тип int всегда 32 разряда или 4 байта.
Таким образом, тип int эквивалентен типам short int или long int в
зависимости от реализации.
Аналогично, тип unsigned int эквивалентен типам unsigned
short или unsigned long. Спецификаторы типов int и unsigned int
широко используются в программах на Си, поскольку они
позволяют наиболее эффективно манипулироватьцелыми
величинами на данной машине.
Однако, размер типов int и unsigned int переменный, поэтому
программы, зависящие от специфики размера int и unsigned int
могут быть непереносимы. Переносимость кода можно улучшить
путем включения выражений с sizeof операцией.
Область значений величин
Область значений величин — это интервал целых величин от
минимального до максимального, который может быть
представлен в заданном числе бит. Например, константное
выражение _32768 состоит из арифметического отрицания _,
предшествующего величине константы 32768. Так как 32768 —
это слишком большое значение для типа short, то оно задается
типом long и следовательно константное выражение _32768 будет
иметь тип long. Величина _32768 может быть представлена как
short только путем преобразования ее типа к типу short.
Информация не будет потеряна при преобразовании типа,
поскольку _32768 может быть представлено двумя байтами
памяти. Аналогично такая величина, как 65000 может быть
представлена как unsigned short только путем преобразования ее к
типу unsigned short или заданием величины в восьмеричном или
шестнадцатеричном представлении. Величина 65000 в
Язык программирования Си
29
десятичном представлении рассматривается как знаковая и
задается типом long, так как 65000 не соответствует типу short. Эта
величина типа long может быть преобразована к типу unsigned
short без потери информации, так как 65000 можно разместить в
двух байтах памяти.
Восьмеричные и шестнадцатеричные константы могут быть
знакового и беззнакового типа в зависимости от их размера.
Однако, метод, используемый для назначения типов этим
константам гарантирует, что они всегда будут преобразованы к
беззнаковому целому. Числа с плавающей точкой используют
IEEE (институт инженеров электриков и электронщиков) формат.
Величины типа float занимают 4 байта, состоящих из бита знака и
7_ми битовой избыточной экспоненты и 24_х битовой мантиссы.
Мантисса представляет число между 1.0 и 2.0. Так как старший
бит мантиссы всегда 1, он не запоминается в памяти. Это
представление дает область значений приблизительно от 3.4E_38
до 3.4E38.
Величины типа double занимают 8 байт. Их формат
аналогичен float формату, за исключением того, что порядок
занимает 11 бит, а мантисса 52 бита плюс бит, который опущен.
Это дает область значений приблизительно от 1.7E_308 до
1.7E+308.
Деклараторы
Синтаксис:
*
(
Си позволяет объявлять: массивы величин, указатели на
величины, величины возвратов функций. Чтобы объявить эти
объекты, нужно использовать декларатор, возможно
модифицированный квадратными скобками [], круглыми
скобками () и звездочкой *, что соответствует типам массива,
функции или указателя. Деклараторы появляются в объявлениях
указателей, массивов и функций.
Язык программирования Си
30
Деклараторы массивов, функций и указателей
Когда декларатор состоит из немодифицируемого
идентификатора, то объект, который объявляется, имеет
немодифицированный тип. Звездочка, которая может появиться
слева от идентификатора, модифицирует его в тип указателя.
Если за идентификатором следуют квадратные скобки [], то тип
модифицируется на тип массива. Если за идентификатором
следуют круглые скобки, то тип модифицируется на тип
функции. Сам по себе декларатор не образует полного
объявления. Для этого в объявление должен быть включен
спецификатор типа. Спецификатор типа задает тип элементов
массива или тип адресуемых объектов и возвратов функции.
Следующие примеры иллюстрируют простейшие формы
деклараторов:
1. int list[20]
2. char *cp
3. double func(void),
где:
1. Массив list целых величин
2. Указатель cp на величину типа char
3. Функция func без аргументов, возвращающая
величину double.
Составные деклараторы
Любой декларатор может быть заключен в круглые скобки.
Обычно, круглые скобки используются для спецификации
особенностей интерпретации составного декларатора. Составной
декларатор — это идентификатор, определяемый более чем одним
модификатором массива, указателя или функции.
С отдельным идентификатором могут появиться различные
комбинации модификаторов массива, указателя или функции.
Некоторые комбинации недопустимы. Например, массив не
может быть композицией функций, а функция не может
возвратить массив или функцию. При интерпретации составных
деклараторов квадратные и круглые скобки (справа от
идентификатора) имеют приоритет перед звездочкой (слева от
идентификатора). Квадратные или круглые скобки имеютодин и
тот же приоритет и рассматриваются слева направо.
Спецификатор типа рассматривается на последнем шаге, когда
Язык программирования Си
31
декларатор уже полностью проинтерпретирован. Можно
использовать круглые скобки, чтобы изменить порядок
интерпретации на необходимый в данном случае.
При интерпретации составных деклараторов может быть
предложено простое правило, которое читается следующим
образом: «изнутри_наружу». Нужно начать с идентификатора и
посмотреть вправо, есть ли квадратные или круглые скобки. Если
они есть, то проинтерпретировать эту часть декларатора, затем
посмотреть налево, если ли звездочка. Если на любой стадии
справа встретится закрывающая круглая скобка, то вначале
необходимо применить все эти правила внутри круглых скобок, а
затем продолжить интерпретацию. На последнем шаге
интерпретируется спецификатор типа. В следующем примере
проиллюстрированы эти правила. Последовательность шагов при
интерпретации перенумерована.
Деклараторы со специальными ключевыми словами
Можно использовать следующие специальные ключевые
слова:
cdecl
far
fortran
huge
near
pascal
Эти ключевые слова используются для изменения смысла
объявлений переменной и функции.
Когда специальное ключевое слово встречается в
деклараторе, то оно модифицирует объект, расположенный
справа от ключевого слова. Допускается более одного ключевого
слова, модифицирующего объект. Например, идентификатор
функции может быть модифицирован двумя ключевыми словами
far и pascal. Порядок ключевых слов в этом случае не важен, то
есть far pascal и pascal far имеют один и тот же смысл. В
различных частях объявления могут быть использованы два или
более ключевых слов для модификации смысла составляющих
частей объявления.
Язык программирования Си
32
Например, в следующем объявлении содержится в
различных местах два ключевых слова far и ключевое слово pascal:
int far * pascal far func(void);
Идентификатор функции func модифицируется ключевыми
словами pascal и far. func возвращает величину, объявленную как
far_указатель на величину типа int.
Как и прежде в любом объявлении могут быть использованы
круглые скобки для изменения порядка интерпретации. Правила
интерпретациисоставных объявлений со специальными
ключевыми словами остаются теми же, что и без них. В
следующих примерах показано использование специальных
ключевых слов в объявлениях.
\*************** Example 1 ********************\
int huge database[65000];
\*************** Example 2 *********************\
char * far * x;
\*************** Example 3 *********************\
double near cdecl calc(double,double);
double cdecl near calc(double,double);
\*************** Example 4 *********************\
char far fortran initlist[INITSIZE];
char far *nextchar, far *prevchar, far *currentchar;
\*************** Example 5 **********************\
char far *(far *getint) (int far *);
^ ^ ^ ^ ^ ^
6 5 2 1 3 4
В первом примере объявляется массив huge, поименованный
database, содержащий 65000 элементов типа int. Декларатор
массива модифицируется ключевым словом huge.
Во втором примере ключевое слово far модифицирует
расположенную справа звездочку, делая x far_указателем на
указатель к величине типа char.
Это объявление эквивалентно и такой записи:
char * (far *x);
В третьем примере показано два эквивалентных объявления.
Оба объявляют calc как функции с near и cdecl атрибутами.
В четвертом примере также представлено два объявления:
первое объявляет far fortan_массив символов, поименованный
Язык программирования Си
33
initlist, а второе объявляет три far_указателя, поименованные
nexchar, prevchar и currentchar. Указатели могут быть
использованы для запоминания адресов символов массива initlist.
Заметим, что ключевое слово far должно быть повторено перед
каждым декларатором.
В пятом примере показано более сложное объявление с
различными случаями расположения ключевого слова far.
Интерпретация этого объявления состоит из следующих шагов:
1. Идентификатор getint объявляется как
2. far_указатель на
3. функцию, требующую
4. один аргумент, который является far_указателем на
величину типа int
5. и возвращающую far_ указатель на
6. величину типа char
Заметим, что ключевое слово far всегда модифицирует
объект, который следует справа.
Объявления переменной
Объявление простой переменной
Синтаксис:
Объявление простой переменной определяет имя
переменной и ее тип; оно может также определять класс памяти
переменной. Имя переменной — это идентификатор, заданный в
объявлении. Спецификатор типа type_specifier задает имя
определяемого типа данных.
Можно определить имена различных переменных в том же
самом объявлении, задавая список идентификаторов,
разделенных запятой. Каждый идентификатор списка именует
переменную. Все переменные, заданные в объявлении, имеют
один и тот же тип.
Примеры:
int x; /* Example 1 */
unsigned long reply, flag /* Example 2 */
Язык программирования Си
34
double order; /* Example 3 */
В первом примере объявляется простая переменная x. Эта
переменная может принимать любое значение из множества
значений, определяемых для типа int.
Во втором примере объявлены две переменные: reply и flag.
Обе переменные имеют тип unsigned long.
В третьем примере объявлена переменная order, которая
имеет тип double. Этой переменной могут быть присвоены
величины с плавающей запятой.
Объявление перечисления
Синтаксис:
enum[
enum
Объявление перечисления задает имя переменной
перечисления и определяет список именованных констант,
называемый списком перечисления. Значением каждого имени
списка является целое число. Переменная перечисления
принимает значение одной из именованных констант списка.
Именованные константы списка имеют тип int. Таким образом,
память соответствующая переменной перечисления — это память,
необходимая для размещения отдельной целой величины.
Объявление перечисления начинается с ключевого слова
enum и имеет две формы представления. В первой форме
представления имена перечисления задаются в списке
перечисления enum_list.
Опция tag — это идентификатор, который именует тип
перечисления, определенного в enum_list.
Переменную перечисления именует identifier. В объявлении
может быть описана более чем одна переменная перечисления.
Во второй форме используется тег перечисления, который
ссылается на тип перечисления. В этой форме объявления список
перечисления не представлен, поскольку тип перечисления
определен в другом месте. Если задаваемый тег не ссылается на
уже определенный тип перечисления, или если именуемый тегом
тип находится вне текущей видимости, то выдается ошибка.
Язык программирования Си
35
[=
.
.
.
Каждый
идентификатор именует элементы
перечисления.
По
умолчанию первому идентификатору
соответствует значение
0,
следующий идентификатор ассоциируется
со значением 1
и
т.д.
Имя константы перечисления эквивалентно
ее значению.
Запись
=
последовательность
значений, заданных по умолчанию.
Идентификатор,
следующий перед записью =
expression>
принимает
значение, задаваемое этим константным
выражением.
Константное выражение имеет тип int
и
может быть
отрицательным.
Следующий идентификатор в списке
ассоциируется
с величиной, равной
он
явно не задается другой величиной.
Перечисление
может содержать повторяющиеся значения
идентификаторов,
но каждый идентификатор должен быть
уникальным.
Кроме того, он должен быть отличным от
всех
других
идентификаторов перечислений с той же
видимостью.
Например,
двум различным идентификаторам null
и
zero
может
быть
задано значение 0 в одном и том же
перечислении.
Идентификаторы
должны быть отличны от других
идентификаторов
с той же самой видимостью, включая имена
обычных
переменных и идентификаторы других
перечислений.
Теги
перечислений должны быть отличны от
тегов перечислений,
тегов
структур и совмещений с той же самой
видимостью.
Примеры:
/****************
Example 1 ***************/
enum
day
saturday,
sunday
= 0,
monday,
tuesday,
wednesday,
thursday,
friday
Язык
программирования Си
36
workday;
/*****************
Example 2 ***************/
enum
day today = wednesday;
В
первом примере определяется тип
перечисления,
поименованный
day
и
объявляется переменная workday
этого
типа
перечисления.
С saturday
по
умолчанию ассоциируется значение
0.
Идентификатор sunday
явно
устанавливается в 0. Оставшиеся
идентификаторы
по умолчанию принимают значение от 1 до
5.
Во
втором примере переменной today
типа
enum
day
присваивается
значение из перечисления. Заметим, что
для
присваивания
используется имя константы из перечисления.
Так
как
тип перечисления day
был
предварительно объявлен, то
достаточно
сослаться только на тег перечисления.
Объявления
структур
Синтаксис:
struct[
struct
Объявление
структуры задает имя типа структуры и
специфицирует
последовательность переменных величин,
называемых
элементами структуры, которые могут
иметь
различные
типы.
Объявление
структуры начинается с ключевого слова
struct
и
имеет
две формы представления, как показано
выше. В первой
форме
представления типы и имена элементов
структуры
специфицируются
в списке объявлений элементов
именует
тип структуры, определенный в списке
объявлений
элементов.
Каждый
структуры.
Тип переменной в деклараторе может быть
модифицирован
на указатель к структуре, на массив
структур или
на
функцию, возвращающую структуру.
Вторая
синтаксическаяформа использует тег —
структуры
для ссылки на тип структуры. В этой форме
объявления
отсутствует список объявлений элементов,
поскольку
тип
структуры определен в другом месте.
Определение типа
структуры
должно быть видимым для тега, который
используется
Язык
программирования Си
37
в
объявлении и определение должно
предшествовать объявлению
через
тег, если тег не используется для
объявления указателя или
структурного
типа typedef.
В последних случаях объявления могут
использовать
тег структуры без предварительного
определения
типа
структуры, но все же определение должно
находиться в
пределах
видимости объявления.
Список
объявлений элементов
это
одно или более объявлений переменных
или битовых полей.
Каждая
переменная, объявленная в этом списке,
называется
элементом
структурного типа. Объявления переменных
списка
имеют
тот же самый синтаксис, что и объявления
переменных, за
исключением
того, что объявления не могут содержать
спецификаторов
класса памяти или инициализаторов.
Элементы
структуры
могут быть любого типа: основного,
массивом,
указателем,
совмещением или структурой.
Элемент
не может иметь тип структуры, в которой
он
появляется.
Однако, элемент может быть объявлен,
как указатель
на
тип структуры, в которую он входит,
позволяя создавать
списочные
структуры.
Битовые
поля
Объявления
битовых полей имеют следующий синтаксис:
Битовое
поле состоит из некоторого числа бит,
специфицированных
константным выражением
expression>.
Для битового поля спецификатор типа
а
константное выражение должно быть
неотрицательной целой
величиной.
Массивы битовых полей, указатели на
битовые поля и
функции,
возвращающие битовые поля не допускаются.
Идентификатор
Неименованное
битовое поле, чей размер специфицируется
как
нулевой,
имеет специальное назначение: оно
гарантирует, что
память
для следующей переменной объявления
будет начинаться
на
границе int.
Идентификаторы
элементов внутри объявляемой структуры
должны
быть уникальными. Идентификаторы
элементов внутри
разных
структур могутсовпадать. В пределах
той же самой
Язык
программирования Си
38
видимости
теги структур должны отличаться от
других тегов
(тегов
других структур, совмещений и перечислений).
Переменные
(элементы) структуры запоминаются
последовательно
в том же самом порядке, в котором они
объявляются:
первой переменной соответствует самый
младший
адрес
памяти, а последней — самый старший.
Память каждой
переменной
начинается на границе свойственной ее
типу.
Поэтому
могут появляться неименованные участки
между
соседними
элементами.
Битовые
поля не располагаются на пересечении
границ,
объявленных
для них типов. Например, битовое поле,
объявленное
с типом unsigned
int,
упаковывается или в
пространстве,
оставшимся от предыдущего unsigned
int или
начиная
с нового unsigned
int.
Примеры:
/****************
Example 1 ****************/
struct
float
x,y;
complex;
/****************
Example 2 *****************/
struct
employee
char
name[20];
int
id;
long
class;
temp;
/****************
Example 3 ******************/
struct
employee student, faculty, staff;
/****************
Example 4 ******************/
struct
sample
char
c;
float
*pf;
struct
sample *next;
x;
/*****************
Example 5 ******************/
struct
unsigned
icon : 8;
unsigned
color : 4;
unsigned
underline : 1;
unsigned
blink : 1;
screen[25][80];
Язык
программирования Си
39
В
первом примере объявляется переменная
с именем
complex
типа
структура.
Эта
структура состоит из двух элементов x
и
y
типа
float.
Тип
структуры
не поименован.
Во
втором примере объявляется переменная
с именем temp
типа
структура. Структура состоит из трех
элементов с именами
name,
id
и
class.
Элемент с именем name
—
это массив из 20_ти
элементов
типа char.
Элементы с именами id
и
class
—
это
простые
переменные типа int
и
long
соответственно.
Идентификатор
employee
является
тегом структуры.
В
третьем примере объявлены три переменных
типа
структура
с именами: student,
faculty
и
staff.
Каждая из структур
состоит
из трех элементов одной и той же
конструкции.
Элементы
определены при объявлении типа структуры
с тегом
employee
в предыдущем примере.
В
четвертом примере объявляется переменная
с именем x
типа
структура. Первые два элемента структуры
представлены
переменной
c типа char
и
указателем pf
на
величину типа float.
Третий
элемент с именем next
объявляются
как указатель на
описываемую
структуру sample.
В
пятом примере объявляется двумерный
массив
поименованный
screen,
элементы которого имеют структурный
тип.
Массив состоит из 2000 элементов и каждый
элемент — это
отдельная
структура, состоящая из четырех элементов
типа bit_fild
с
именами icon,
color,
underline
и
blink.
Объявление
совмещений
Синтаксис:
union[
[,
union
Объявление
совмещения определяет имя переменной
совмещения
и специфицирует множество переменных,
называемых
элементами совмещения, которые могут
быть
различных
типов. Переменная с типом совмещения
запоминает
любую
отдельную величину, определяемую набором
элементов
совмещения.
Язык
программирования Си
40
Объявление
совмещения имеет тот же самый синтаксис,
как
и
объявление структуры, за исключением
того, что она
начинается
с ключевого слова union
вместо
ключевого слова
struct.
Для объявления совмещения и структуры
действуют одни и
те
же правила, за исключением того, что в
совмещении не
допускаются
элементы типа битовых полей.
Память,
которая соответствует переменной типа
совмещение,
определяется величиной для размещения
любого
отдельного
элемента совмещения.
Когда
используется наименьший элемент
совмещения, то
переменная
типа совмещения может содержать
неиспользованное
пространство.
Все элементы совмещения запоминаются
в одном
и
том же пространстве памяти переменной,
начиная с одного и
того
же адреса. Запомненные значения затираются
каждый раз,
когда
присваивается значение очередного
элемента совмещения.
Объявление
массива
Синтаксис:
Здесь
квадратные скобки — это терминальные
символы.
Объявление
массива определяет тип массива и тип
каждого
элемента.
Оно может определять также число
элементов в
массиве.
Переменная типа массив рассматривается
как указатель
на
элементы массива. Объявление массива
может представляться
в
двух синтаксических формах, указанных
выше.
Декларатор
скобки,
следующие за декларатором, модифицируют
декларатор
на
тип массива. Константное выражение
заключенное
в квадратные скобки, определяет число
элементов в
массиве.
Каждый элемент имеет тип, задаваемый
спецификатором
типа
специфицировать
любой тип, исключая void
и
тип функции.
Во
второй синтаксической форме опущено
константное
выражение
в квадратных скобках. Эта форма может
быть
использована
только тогда, когда массив инициализируется
или
объявлен
как формальный параметр или объявлен
как ссылка на
массив,
явно определенныйгде_то в программе.
Язык
программирования Си
41
Массив
массивов или многомерный массив
определяется
путем
задания списка константных выражений
в квадратных
скобках,
следующего за декларатором:
[
Каждое
константное выражение
квадратных
скобках определяет число элементов в
данном
измерении
массива, так что объявление двумерного
массива
содержит
два константных выражения, трехмерного
— три и т.д.
Если
многомерный массив объявляется внутри
функции или если
он
инициализируется либо объявляется как
формальный
параметр
или объявляется как ссылка на массив,
явно
определенный
где_то в программе, то первое константное
выражение
может быть опущено.
Массив
указателей на величины заданного типа
может быть
определен
посредством составного декларатора.
Типу
«массив» соответствует память, которая
требуется для
размещения
всех его элементов. Элементы массива с
первого до
последнего
запоминаются в последовательных
возрастающих
адресах
памяти. Между элементами массива в
памяти разрывы
отсутствуют.
Элементы массива запоминаются друг за
другом
построчно.
Например, массив, содержащий две строки
с тремя
столбцами
каждая, char
A[2][3] будет
запомнен следующим
образом.
Сначала запоминаются три столбца первой
строки,
затем
элементы трех столбцов второй строки.
Смысл этого в том,
чтобы
последний индекс был более быстрым.
Чтобы сослаться на
отдельный
элемент массива, нужно использовать
индексное
выражение.
Объявление
указателей
Синтаксис:
Объявление
указателя определяет имя переменной
типа
указатель
и тип объекта, на который указывает эта
переменная.
Декларатор
модификацией
ее типа. Спецификатор типа
задает
тип объекта, который может быть базового
типа, типа
структуры
или совмещения.
Язык
программирования Си
42
Переменная
типа указатель может указывать также
на
функции,
массивы и другие указатели.
Если
указатель не используется до определения
типа
структуры
или совмещения, то он может быть объявлен
ранее
этого
определения. Такие объявления допускаются,
поскольку
компилятору
не требуется знать размер структуры
или
совмещения,
чтобы распределить память под переменную
типа
указатель.
Указатель может быть объявлен посредством
использования
тега структуры или совмещения.
Переменная,
объявленная как указатель, хранит адрес
памяти.
Размер памяти, требуемый для адреса, и
смысл адреса
зависит
от данной конфигурации машины. Указатели
на
различные
типы не обязательно имеют одну и ту же
длину.
Для
некоторых реализаций используются
специальные
ключевые
слова near,
far
и
huge,
чтобы модифицировать размер
указателя.
Как
объявляются функции
Синтаксис:
[
[,
Объявление
функции определяет имя, тип возврата
функции
и, возможно, типы и число ее аргументов.
Объявление
функции
также называется forward_объявлением.
Декларатор
функции
объявляет имя функции, а спецификатор
типа задает
тип
возврата. Если спецификатор типа опущен
в объявлении
функции,
то предполагается, что функция возвращает
величину
типа
int.
Объявление
функции может включать спецификаторы
класса
памяти extern
или
static.
Список
типов аргументов
Список
типов аргументов
типы
аргументов функции. Синтаксис списка
аргументов
следующий:
Список
имен типов — это список из одного или
более имен
типов.
Каждое имя типа отделяется от другого
запятой. Первое
имя
типа задает тип первого аргумента,
второе имя типа задает
Язык
программирования Си
43
тип
второго аргумента и т. д. Если список
имен типов
заканчивается
запятой с многоточием (,...),
то это означает, что
число
аргументов функции переменно. Однако,
предполагается,
что
функция будет иметь не меньше аргументов,
чем имен типов,
предшествующих
многоточию.
Если
список типов аргументов
только
многоточие, то число аргументов функции
является
переменным
или равно нулю.
Важно:
Чтобы
поддержать совместимость с программами
предыдущих
версий, компилятор допускает символ
запятой без
многоточия
в конце списка типов аргументов для
обозначения их
переменного
числа. Запятая может быть использована
и вместо
многоточия
для объявления нуля или более аргументов
функции.
Использование
запятой поддерживается только для
совместимости.
Использование многоточия рекомендуетсядля
нового
представления.
Имя
типа
или
базового типа состоит из спецификатора
этого типа (такого
как
int).
Имена типов для указателей, массивов и
функций
формируются
путем комбинации спецификатора типа с
абстрактным
декларатором. Абстрактный декларатор
— это
декларатор
без идентификатора.
Для
того чтобы объявить функцию, не имеющую
аргументов,
может быть использовано специальное
ключевое
слово
void
на
месте списка типов аргументов. Компилятор
вырабатывает
предупреждающее сообщение, если в вызове
такой
функции
будут специфицированы аргументы.
Еще
одна специальная конструкция допускается
в списке
типов
аргументов. Это фраза void
*,
которая специфицирует
аргумент
типа указатель. Эта фраза может быть
использована в
списке
типов аргументов вместо имени типа.
Список
типов аргументов может быть опущен. В
этом случае
скобки
после идентификатора функции все же
требуются, хотя
они
и пусты. В этом случае в объявлении
функции не
определяются
ни типы, ни число аргументов в функции.
Когда
эта
информация опускается, то компилятор
не проверяет
соответствия
между формальными и фактическими
параметрами
при
вызове функции.
Язык
программирования Си
44
Тип
возврата
Функции
могут возвращать величины любого типа
за
исключением
массивов и функций. Для этого посредством
спецификатора
типа type_specifier
в
объявлении функции можно
специфицировать
любой тип: основной, структуру или
совмещение.
Идентификатор функции может быть
модифицирован
одной или несколькими звездочками (*),
чтобы
объявить
возвращаемую величину типа указателя.
Хотя
функции и не допускают возвратов массивов
или
функций,
но они могут возвращать указатели на
массивы или
функции.
Функции, которые возвращают указатели
на величины
типа
массив или функция, объявляются
посредством
модификации
идентификатора функции квадратными
скобками,
звездочкой
и круглыми скобками, чтобы сформировать
составной
декларатор.
Классы
памяти
Класс
памяти переменной, которая определяет
какой либо
объект,
имеет глобальное или локальное время
жизни. Объект с
глобальным
временем жизни существует и имеет
значение на
протяжении
всей программы. Все функции имеют
глобальное
время
жизни.
Переменные
с локальным временем жизни захватывают
новую
память при каждом выполнении блока, в
котором они
определены.
Когда управление на выполнение передается
из
блока,
то переменная теряет свое значение.
Хотя
Си определяет два типа классов памяти,
но, тем не
менее,
имеется следующих четыре спецификатора
классов
памяти:
auto
register
static
extern
Объекты
классов auto
и
register
имеют
локальное время
жизни.
Спецификаторы static
и
extern
определяют
объекты с
глобальным
временем жизни. Каждый из спецификаторов
класса
памяти
имеет определенный смысл, который влияет
на видимость
функций
и переменных в той же мере, как и сами
классы памяти.
Язык
программирования Си
45
Термин
«видимость» относится к той части
программы, в которой
могут
ссылаться друг на друга функции и
переменные. Объекты с
глобальным
временем жизни существуют на протяжении
выполнения
исходной программы, но они могут быть
видимы не
во
всех частях программы.
Месторасположение
объявления переменной или функции
внутри
исходных файлов также влияют на класс
памяти и
видимость.
Говорят, что объявления вне определения
всех
функций
и переменных относятся к внешнему
уровню, а
объявления
внутри определений функций относятся
к
внутреннему
уровню.
Точный
смысл каждого спецификатора класса
памяти
зависит
от того, находится ли объявление на
внешнем или
внутреннем
уровне и от того, объявлен ли объект
функцией или
переменной.
Объявления
переменной на внешнем уровне
Объявления
переменной на внешнем уровне используют
спецификаторы
класса памяти static
и
extern
или
совсем опускают
их.
Спецификаторы класса памяти auto и register
не
допускаются
на
внешнем уровне.
Объявления
переменных на внешнем уровне — это
определения
переменных или ссылки на определения,
сделанные
в
другом месте.
Объявление
внешней переменной, которое инициализирует
эту
переменную (явно или неявно), называется
определением
этой
переменной. Определение на внешнем
уровне может
задаваться
в различных формах.
Переменная
на внешнем уровне может быть определена
путем
ее объявления со спецификатором класса
памяти static.
Такая
переменная можетбыть явно инициализирована
константным
выражением. Если инициализатор
отсутствует, то
переменная
автоматически инициализируется нулем
во время
компиляции.
Таким образом, объявления static
int k = 16 и
static
int k —
оба рассматриваются как определения.
Переменная
определяется, когда она явно
инициализируется
на внешнем уровне. Например, int
j = 3 —
это
определение
переменной.
Язык
программирования Си
46
Так
как переменная определяется на внешнем
уровне, то
она
видима в пределах остатка исходного
файла, от места, где она
определена.
Переменная не видима выше своего
определения в
том
же самом исходном файле ни в других
исходных файлах
программы,
если не объявлена ссылка, которая делает
ее
видимой.
Переменная
может быть определена на внешнем уровне
внутри
исходного файла только один раз. Если
задается
спецификатор
класса памяти static,
то в других исходных файлах
могут
быть определены переменные с тем же
именем. Так как
каждое
определение static
видимо
только в пределах своего
собственного
исходного файла, то конфликта не
возникнет.
Спецификатор
класса памяти extern
используется
для
объявления
ссылки на переменную, определенную
где_то в
другом
месте. Такие объявления используются
в случае, когда
нужно
сделать видимым определение переменной
в других
исходных
файлах или выше места, где она определена
в том же
самом
исходном файле. Так как ссылка на
переменную объявлена
на
внешнем уровне, то переменная видима в
пределах остатка
исходного
файла от места объявления ссылки.
В
объявлениях, которые используют
спецификатор класса
памяти
extern,
инициализация не допускается, так как
они
ссылаются
на переменные, чьи величины уже определены.
Переменная,
на которую делается ссылка extern,
должна
быть
определена на внешнем уровне только
один раз.
Определение
может быть сделано в любом из исходных
файлов,
составляющих
программу.
Есть
одно исключение из правил, описанных
выше. Можно
опустить
из объявления переменной на внешнем
уровне
спецификатор
класса памяти и инициализатор. Например,
объявление
int
n; будет
правильным внешним объявлением. Это
объявление
имеет два различных смысла в зависимости
от
контекста.
1.
Если где_нибудь в программебудет
определена на
внешнем
уровне переменная с тем же именем, то
объявление
является
ссылкой на эту переменную, как если бы
был
использован
спецификатор класса памяти extern
в
объявлении.
Язык
программирования Си
47
2.
Если нет такого определения, то объявленной
переменной
распределяется
память во время линкования и переменная
инициализируется
нулем. Если в программе появится более
чем
одно
такое объявление, то память распределится
для наибольшего
размера
из объявленных переменных. Например,
если программа
содержит
два неинициализированных объявления
переменной i
на
внешнем уровне int
i; и
char
i; то
память во время линкования
распределится
под переменную i
типа
int.
Неинициализированные
объявления переменной на
внешнем
уровне не рекомендуются для файлов,
которые могут
быть
размещены в библиотеку.
Пример:
/***********************************************
SOURCE
FILE ONE
**********************************************/
extern
int i; /* reference to i
defined
below */
main()
i++;
printf("%d\n",
i); /* i equals 4 */
next();
int
i = 3; /* definition of i */
next()
i++;
printf("%d\n",
i); /* i equals 5 */
other();
/***********************************************
SOURCE
FILE TWO
***********************************************/
extern
int i; /* reference to i in
first
source file */
other()
i++;
printf("%d\n",
i); /* i equals 6 */
Язык
программирования Си
48
Объявление
переменной на внутреннем уровне
Любой
из четырех спецификаторов класса памяти
может
быть
использован для объявления переменной
на внутреннем
уровне.
Если спецификатор класса памяти
опускается в
объявлении
переменной на внутреннем уровне, то
подразумевается
класс памяти auto.
Спецификатор
класса памяти auto
объявляет
переменную с
локальным
временем жизни. Переменная видима только
в том
блоке,
где она объявлена. Объявления переменных
auto
могут
включать
инициализаторы. Переменные класса
памяти auto
автоматически
не инициализируются, а инициализируются
явно
при
объявлении или присваивании начальных
значений,
посредством
операторов внутри блока. Если нет
инициализации,
то
величина переменной auto
считается
неопределенной.
Спецификатор
класса памяти register
сообщает
компилятору
о
том, чтобы он распределил память под
переменную в регистре,
если
это возможно. Использование регистровой
памяти обычно
приводит
к более быстрому времени доступа и к
меньшему
размеру
результирующего кода. Переменные,
объявленные с
классом
памяти register
имеют
ту же самую видимость, что и
переменные
auto.
Число
регистров, которое может быть использовано
под
память
переменных, зависит от машины. Когда
компилятор
встречает
спецификатор класса памяти register
в
объявлении, а
свободного
регистра не имеется, то для переменной
распределяется
память класса auto.
Компилятор назначает
переменным
регистровую память в том порядке, в
котором
появляются
объявления в исходном файле. Регистровая
память,
если
она имеется, гарантирована только для
целого и адресного
типов.
Переменная,
объявленная на внутреннем уровне со
спецификатором
класса памяти static,
имеет глобальное время
жизни
и имеет видимость только внутри блока,
в котором она
объявлена.
В отличие от переменных auto,
переменные,
объявленные
как static,
сохраняют свое значение при завершении
блока.
Переменные
класса памяти static
могут
быть
инициализированы
константным выражением. Если явной
инициализации
нет, то переменная класса памяти static
Язык
программирования Си
49
автоматически
устанавливается в 0.
Инициализация выполняется
один
раз во время компиляции. Инициализация
переменной
класса
памяти static
не
повторяется при новом входе в блок.
Переменная,
объявленная со спецификатором класса
памяти
extern,
является ссылкой на переменную с тем
же самым
именем,
определенную на внешнем уровне в любом
исходном
файле
программы.
Цель
внутреннего объявления extern
состоит
в том, чтобы
сделать
определение переменной внешнего уровня
видимой
внутри
блока. Внутреннее объявление extern
не
изменяет
видимость
глобальной переменной в любой другой
части
программы.
Пример:
int
i = 1;
main()
/*
reference to i, defined above */
extern
int i;
/*
initial value is zero; a is
visible
only within main */
static
int a;
/*
b is stored in a register, if possible */
register
int b = 0;
/*
default storage class is auto */
int
c = 0;
/*
values printed are 1, 0, 0, 0 */
printf("%d\n%d\n%d\n%d\n",
i, a, b, c);
other();
other()
/*
i is redefined */
int
i = 16;
/*
this a is visible only within other */
static
int a = 2;
a
+= 2;
/*
values printed are 16, 4 */
printf("%d\n%d\n",
i, a);
Переменная
i
определяется
на внешнем уровне с
инициализацией
1.
В функции main
объявлена
ссылка extern
на
Язык
программирования Си
50
переменную
i
внешнего
уровня. Переменная класса памяти static
автоматически
устанавливается в 0,
так как инициализатор
опущен.
Вызов функции print
(предполагается,
что функция print
определена
в каком_то месте исходной программы)
печатает
величины
1,
0,
0,
0.
В
функции other,
переменная i
переопределяется
как
локальная
переменная с начальным значением 16.
Это не влияет
на
значение внешней переменной i.
Переменная a
объявляется
как
переменная класса памяти static
с
начальным значением 2.
Она
не противоречитпеременной a,
объявленной в функции
main,
так как видимость переменных класса
памяти static
на
внутреннем
уровне ограничена блоком, в котором она
объявлена.
Объявление
функции на внешнем и внутреннем уровнях
Функции
могут быть объявлены со спецификаторами
класса
памяти
static
или
extern.
Функции всегда имеют глобальное время
жизни.
Правила
видимости для функций отличаются от
правил
видимости
для переменных. Объявления функций на
внутреннем
уровне
имеют тот же самый смысл, что и объявления
на внешнем
уровне.
Это значит, что функции не могут иметь
блочной
видимости
и видимость функций не может быть
вложенной.
Функция
объявленная как static,
видима только в пределах
исходного
файла, в котором она определяется. Любая
функция в
том
же самом исходном файле может вызвать
функцию static,
но
функции
static
из
других файлов нет. Функция static
с
тем же
самым
именем может быть объявлена в другом
исходном файле.
Функции,
объявленные как extern
видимы
в пределах всех
исходных
файлов, которые составляют программу.
Любая
функция
может вызвать функцию extern.
Объявления
функций, в которых опущен спецификатор
класса
памяти, считаются по умолчанию extern.
Инициализация
В
объявлении переменной может быть
присвоено начальное
значение
посредством инициализатора. Величина
или величины
инициализатора
присваиваются переменной.
Синтаксически
записи инициализатора предшествует
знак
Язык
программирования Си
51
равно
(=):
=
Могут
быть инициализированы переменные любого
типа.
Функции
не инициализируются. Объявления, которые
используют
спецификатор класса памяти extern
не
могут
содержать
инициализатора.
Переменные,
объявленные на внешнем уровне, могут
быть
инициализированы.
Если они явно не инициализированы, то
они
устанавливаются
в нуль во время компиляции или линкования.
Любая
переменная, объявленная со спецификатором
класса
памяти
static,
может быть инициализирована константным
выражением.
Инициализация переменных класса static
выполняется
один раз во время компиляции. Если
отсутствует
явная
инициализация, то переменные класса
памяти static
автоматически
устанавливаются в нуль.
Инициализация
переменных auto
и
register
выполняется
_________каждый
раз при входе в блок, в котором они
объявлены. Если
инициализатор
опущен в объявлении переменной класса
памяти
auto
или
register,
то начальное значение переменной не
определено.
Инициализация составных типов auto
(массив,
структура,
совмещение) запрещена. Любое составное
объявление
класса
памяти static
может
быть инициализировано на внешнем
уровне.
Начальными
значениями для внешних объявлений
переменной
и для всех переменных static
как
внешних так и
внутренних
должно быть константное выражение.
Автоматические
и регистровые переменные могут быть
инициализированы
константными или переменными
величинами.
Базовые
типы и типы указателей
Синтаксис:
=
Величина
выражения присваивается переменной.
Для
выражения
допустимы правила преобразования.
Язык
программирования Си
52
Составные
типы
Синтаксис:
=
Список
инициализаторов
последовательность
инициализаторов, разделенных запятыми.
Каждый
инициализатор в последовательности —
это либо
константное
выражение, либо список инициализаторов.
Поэтому,
заключенный
в фигурные скобки список, может появиться
внутри
другого списка инициализации. Эта
конструкция
используется
для инициализации элементов составных
конструкций.
Для
каждого списка инициализации значения
константных
выражений
присваиваются в порядке следования
элементов
составной
переменной. Когда инициализируется
совмещение, то
список
инициализаторов представляет собой
единственное
константное
выражение. Величина константного
выражения
присваивается
первому элементу совмещения.
Если
в списке инициализации меньше величин,
чем их
имеется
в составном типе, то оставшиеся памяти
инициализируются
нулем. Если число инициализирующих
величин
больше чем требуется, то выдается ошибка.
Эти
правила применяются к каждому вложенному
списку
инициализаторов,
точно так же как и ко всей конструкции
в
целом.
Эти
дополнительные запятые допускаются,
но не требуются.
Требуются
только те запятые, которые разделяют
константные
выражения
и списки инициализации. Если список
инициализаторов
не структурирован под составной объект,
то его
величины
присваиваютсяв том порядке, в котором
подстыкованы
элементы объекта.
Фигурные
скобки могут также появляться вокруг
индивидуальных
инициализаторов в списке.
Когда
инициализируются составные переменные,
то нужно
позаботиться
о том, чтобы правильно использовать
фигурные
скобки
и списки инициализаторов. В следующем
примере
иллюстрируется
более детально интерпретация компилятором
фигурных
скобок.
Язык
программирования Си
53
Строковые
инициализаторы
Массив
может быть инициализирован строчным
литералом.
Например,
char
code[ ] = "abc";
инициализирует
code
как
массив символов из четырех элементов.
Четвертым
элементом является символ \0,
который завершает все
строковые
литералы.
Если
специфицируется размер массива, а строка
больше чем
специфицированный
размер, то лишние символы отбрасываются.
Следующее
объявление инициализирует переменную
code,
как
трехэлементный
массив символов:
char
code[3] = "abcd"
В
примере только три первые символа
инициализатора
назначаются
для массива code.
Символ d
и
символ нуль
отбрасываются.
Если
строка короче, чем специфицированный
размер
массива,
то оставшиеся элементы массива
инициализируются
нулем
(символом \0).
Объявления
типов
Объявление
типа определяет имя и элементы структурного
или
совмещающего типов или имя и перечислимое
множество
перечислимого
типа.
Имя
типа может быть использовано в объявлениях
переменных
и функций в качестве ссылки на этот тип.
Это
полезно,
когда многие переменные или функции
имеют один и
тот
же тип.
Объявление
typedef
определяет
спецификатор типа для типа.
Это
объявление используется для того, чтобы
создавать более
короткие
или более осмысленные имена типов уже
определенных
в
Си или объявленных пользователем.
Типы
структур, совмещений и перечислений
Объявления
типов структур, совмещений и перечислений
имеют
ту же самую общую синтаксическую форму,
как и
объявления
переменных этих типов. В объявлении
типа
идентификатор
переменной опущен, так как нет переменной
Язык
программирования Си
54
которая
объявляется. Именем структуры, совмещения
или
перечисления
является тег.
В
объявлении типа может появиться список
объявлений
элементов
Сокращенная
форма объявления переменной, в котором
tag
ссылается
на тип, определенный где_то еще, при
объявлении типа
не
используется.
Объявления
typedef
Синтаксис:
typedef
Объявления
typedef
являются
аналогом объявления
переменной,
за исключением того, что ключевое слово
typedef
заменяет
спецификатор класса памяти.
Объявление
интерпретируется тем же самым путем,
как
объявления
переменной или функции, но
того,
чтобы стать переменной типа,
специфицированного
объявлением,
становится синонимом имени типа.
Объявление
typedef
не
создает типов. Оно создает синонимы для
существующих
имен типов, которые были специфицированы
другим
способом. Любой тип может быть объявлен
с typedef,
включая
типы указателя, функции и массива. Имя
с ключевым
словом
typedef
для
типов указателя, структуры или совмещения
может
быть объявлено прежде чем эти типы будут
определены, но
в
пределах видимости объявления.
Имена
типов
Имя
типа специфицирует особенности типа
данных. Имена
типов
используются в трех контекстах: в списках
типов
аргументов,
при объявлении функций, в вычислениях
cast
(преобразованиях
типов), и в sizeof
операциях.
Именами
для основных, перечисляющих, структурных
и
совмещающих
типов являются спецификаторы типа для
каждого
из
них. Имена для типов указателя, массива
и функции задаются
следующей
синтаксической формой:
Язык
программирования Си
55
Абстрактный
декларатор
декларатор
без идентификатора, состоящий из одного
или более
модификаторов
указателей, массивов и функций. Модификатор
указателя
*
всегда
появляется перед идентификатором в
деклараторе,
в то время как модификатор массива []
или
функции
()
появляются
после идентификатора. Таким образом,
чтобы
правильно
интерпретировать абстрактный декларатор,
нужно
начинать
интерпретацию с подразумеваемого
идентификатора.
Абстрактные
деклараторы могут быть составными.
Скобки в
составном
абстрактном деклараторе специфицируют
порядок
интерпретации,
подобно тому как это делается при
интерпретации
составных деклараторов объявлений.
Абстрактный
декларатор, состоящий из пустых
круглыхскобок ()
не
допускается, поскольку это двусмысленно.
В этом случае
невозможно
определить находится ли подразумеваемый
идентификатор
внутри скобок, и в таком случае — это
немодифицированный
тип, или перед скобками, тогда — это тип
функции.
Спецификаторы типа, установленные
посредством
объявлений
typedef,
также рассматриваются как имена типов.
Выражения
и присваивания
В
Си присваиваются значения выражений.
Помимо
простого
присваивания посредством операции =,
Си
поддерживает
составные операции присваивания, которые
перед
присваиванием
выполняют дополнительные операции над
своими
операндами. Окончательное значение
результата зависит
от
старшинства операций и от побочных
эффектов, если они
возникают.
Порядок вычисления устанавливается
определенным
группированием
операндов и операций в выражении.
Побочный
эффект
— это изменения состояния машины,
вызванные в
процессе
вычисления выражения.
В
выражении с побочным эффектом, вычисление
одного
операнда
может зависеть от значения другого. Для
одних и тех же
операций
от порядка, в котором вычисляются
операнды, также
зависит
результат выражения.
Величина,
представляемая каждым операндом в
выражении,
имеет
тип, который может быть преобразован к
другим типам в
определенном
контексте. Преобразования типов имеют
место в
Язык
программирования Си
56
присваиваниях,
cast
операциях,
вызовах функций и при
выполнении
операций.
Операнды
Операнд
в Си — это константа, идентификатор,
строка,
вызов
функции, индексное выражение, выражение
выбора
структурного
элемента или более сложное выражение,
сформированное
комбинацией операндов и операций или
заключением
операндов в скобки. Любой операнд,
который имеет
константное
значение, называется константным
выражением.
Каждый
операнд имеет тип. Операнд может быть
преобразован
из оригинального типа к другому типу
посредством
операции
преобразования типов. Выражение
преобразования
типа
может быть использовано в качестве
операнда выражения.
Константы
Операнду_константе
соответствует значение и тип
представляющей
его константы. Константа_символ имеет
тип int.
Целая
константа имеет типы: int,
long,
unsigned
int или
unsigned
long,
в зависимости от размера целого и от
того как
специфицирована
его величина. Константы с плавающей
точкой
всегда
имеют тип double.
Идентификаторы
Идентификаторы
именуют переменные и функции. Каждый
идентификатор
имеет тип, который устанавливается при
его
объявлении.
Значение идентификатора зависит от
типа
следующим
образом:
? идентификаторы
целых и плавающих типов представляют
величины
соответствующего типа.
? идентификатор
перечисляющего типа представляет
значение
одной константы из множества значений
констант
в перечислении. Значение идентификатора
равно
значению этой константы. Тип значения
есть int,
что
следует из определения перечисления.
? идентификатор
структурного или совмещающего типов
представляет
величины, специфицированные
в
структуре или совмещении.
Язык
программирования Си
57
? идентификатор,
объявленный как указатель,
представляет
указатель на величину специфицированного
типа.
? идентификатор,
объявленный как массив, представляет
указатель,
чье значение является адресом первого
элемента
массива. Тип адресуемых указателем
величин —
это
тип элементов массива. Например, если
series
объявлен
как массив целых из 10_ти элементов, то
идентификатор
series
представляет
адрес массива, тогда
как
индексное выражение series[5]
ссылается
на шестой
элемент
массива. Адрес массива не изменяется
во время
выполнения
программы, хотя значения отдельных
элементов
могут изменяться. Значение указателя,
представленное
идентификатором массива, не является
переменной
и поэтому идентификатор массива не
может
появляться
в левой части операции присваивания.
? идентификатор,
объявленный как функция, представляет
указатель,
чье значение является адресом функции.
Тип,
адресуемый указателем, — это
специфицированный
тип
функционального возврата. Адрес функции
не
изменяется
во время выполнения программы. Меняется
только
значение возврата. Таким образом,
идентификаторы
функции не могут появляться в левой
части
операции присваивания.
Строки
Строковый
литерал состоит из последовательности
символов,
заключенных в двойные кавычки.
Строковыйлитерал
представляется
в памяти как массив элементов типа char.
Строковый
литерал представляет адрес первого
элемента этого
массива.
Адрес первого элемента строки является
константой, так
же
как и сама строка.
Так
как строковые литералы — это полноценные
указатели,
то
они могут быть использованы в контексте,
допускающем
величины
типа указателей, подчиняясь при этом
тем же самым
ограничениям.
Строковые литералы имеют все же одно
дополнительное
ограничение: они не изменяемы и не могут
появиться
в левой части операции присваивания.
Язык
программирования Си
58
Последним
символом строки всегда является символ
нуль
0.
Символ
нуль не видим в строковом выражении, но
он
добавляется
как последний элемент, когда строка
запоминается.
Таким
образом, строка abc
содержит
четыре символа, а не три.
Вызовы
функций
Синтаксис:
Вызов
функции состоит из выражения
которым
следует список выражений
выражения
соответствует адрес функции (например,
значение
идентификатора
функции). Значение каждого выражения
из
списка
выражений (выражения в списке разделены
запятыми)
соответствует
фактическому аргументу функции. Список
выражений
может быть пустым.
Выражение
вызова функции имеет значение и тип
своего
возврата.
Если тип возврата функции void,
то и выражение вызова
функции
имеет тип void.
Если возврат из вызванной функции
произошел
не в результате выполнения оператора
return,
то
значение
функции не определено.
Индексные
выражения
Синтаксис:
Здесь
квадратные скобки — это терминальные
символы.
Индексное
выражение представляет величину, адрес
которой
состоит
из суммы значений выражения1
выражения2
—
указатель,
такой как идентификатор массива, а
выражение2 — это
целочисленная
величина. Выражение2 должно быть заключено
в
квадратные
скобки [].
Индексное
выражение обычно используется для
ссылок на
элементы
массива, тем не менее, индекс может
появиться с
любым
указателем.
Индексное
выражение вычисляется путем сложения
целой
величины
последующим
применением к результату операции
разадресации
*.
Например,для одномерного массива
следующие четыре
Язык
программирования Си
59
выражения
эквивалентны в предположении, что a
—
это
указатель,
а b
—
это целое.
a[b]
*(a
+ b)
*(b
+ a) b[a]
В
соответствии с правилами преобразования
типов для
операции
сложения, целочисленная величина
преобразуется к
адресному
представлению путем умножения ее на
размер типа,
адресуемого
указателем. Например, предположим, что
идентификатор
line
ссылается
на массив величин типа int.
Чтобы
вычислить
выражение line[i],
целая величина i
умножается
на
размер
типа int.
Преобразованное значение i
представляет
i
позиций
типа int.
Это преобразованное значение складывается
с
начальным
значением указателя line,
что дает адрес, который
расположен
на i
позиций
типа int
от
line.
Последним
шагом вычисления индексного выражения
является
операция разадресации, применяемая к
полученному
адресу.
Результатом является значение элемента
массива,
который
позиционирован.
Заметим,
что индексное выражение line[0]
представляет
значение
первого элемента массива, так как отсчет
смещения
ведется
от нуля.
Следовательно,
такое выражение, как line[5],
ссылается на
шестой
элемент массива.
Ссылки
на многомерный массив
Индексное
выражение может быть снова проиндексировано.
Синтаксис
такого выражения следующий:
Данное
индексное выражение интерпретируется
слева
направо.
Сначала вычисляется самое левое индексное
выражение
которым
складывается
разадресации
осуществляется после вычисления
последнего
индексного
выражения. Однако, операции разадресации
не
производится,
если значение последнего указателя
адресует
величину
типа массив.
Язык
программирования Си
60
Выражения
с несколькими индексами ссылаются на
элементы
многомерных массивов. Многомерный
массив — это
массив,
элементами которого являются массивы.
Например,
первым
элементом трехмерного массива является
массив с двумя
измерениями.
Выражения
с операциями
Выражения
с операциями могут быть унарными,
бинарными
или тернарными. Унарное выражение
состоит из
операнда
с предшествующей унарнойоперацией
операнда,
заключенного в круглые скобки, с
предшествующим
ему
ключевым словом sizeof.
Синтаксис:
sizeof
Бинарное
выражение состоит из двух операндов,
разделенных
бинарной операцией
Синтаксис:
Тернарное
выражение состоит из трех операндов,
разделенных
тернарной операцией ?:
Синтаксис:
Выражения
присваивания используют унарные,
бинарные и
составные
операции присваивания. Унарными
операциями
присваивания
являются инкремент ++
и
декремент __.
Бинарная
операция
присваивания всего одна =.
Составные операции
присваивания
будем обозначать как
Каждая
составная операция присваивания — это
комбинация
бинарной
операции с простой операцией присваивания.
Синтаксис
выражений присваивания:
++
__
Язык
программирования Си
61
Выражения
в скобках
Любой
операнд может быть заключен в скобки.
Они не
влияют
на тип и значение выражения, заключенного
в скобки.
Например,
в выражении (10
+ 5) / 5 скобки,
заключающие запись
10
+ 5,
означают, что величина 10
+ 5 является
левым операндом
операции
деления. Результат выражения (10
+ 5) / 5 равен
3.
Без
скобок
значение записи 10
+ 5 / 5 равнялось
бы 11.
Хотя скобки
влияют
на то, каким путем группируются операнды
в выражении,
они
не гарантируют детальный порядок
вычисления выражения.
Type!cast
выражения
Type_cast
выражения имеют следующий синтаксис:
(
Константные
выражения
Константное
выражение — это выражение, результатом
вычисления
которого является константа. Операндами
константного
выражения могут быть целые константы,
константы
символы,
плавающие константы, константы
перечисления,
type_cast
выражения
целого и плавающего типов и другие
константные
выражения. Операнды могут комбинироваться
и
модифицироваться
посредством операций.
Константные
выражения не могут использовать операции
присваивания
или бинарную операцию последовательного
вычисления.
Унарная операция адресации &
может
быть
использована
только при некоторых инициализациях.
Константные
выражения, используемые в директивах
препроцессора,
имеют дополнительные ограничения,
поэтому
называются
ограниченнымиконстантными выражениями:
Ограниченные
_________константные
выражения не могут содержать