Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
lektsii_po_si.doc
Скачиваний:
13
Добавлен:
12.11.2018
Размер:
11.78 Mб
Скачать

Препроцессорные средства

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

Несколько нетрадиционно для пособий по языку Си начнем дальнейшее изложение материала о возможностях препроцессора. Это позволит в следующих главах продемонстрировать эффективность препроцессорных средств и их применимость при решении разнородных задач. Откладывать, как часто принято, изучение препроцессора на конец курса по языку Си, по нашему мнению, не совсем удачно. Даже в начальных (вступительных) главах 1 и 2 пришлось затронуть некоторые важные вопросы препроцессорной обработки. Введены константы, определяемые директивой #define, проиллюстрированы некоторые правила включения в программу текстов заголовочных файлов для связи со стандартной библиотекой компилятора. Этих сведений вполне достаточно, чтобы программировать на языке Си, вводить с помощью #define собственные обозначения констант и не испытывать затруднений в использовании стандартных библиотек.

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

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

Итак, на входе препроцессора - текст с препроцессорными директивами, на выходе препроцессора - текст без препроцессорных директив (см. рис. 1.1).

Глава 6. Структуры и объединения

6.1. Структурные типы и структуры Производные типы.

Производные типы. Из базовых типов (и типов, определенных программистом) можно формировать производные типы, к которым относятся: указатели, массивы, функции, структуры и объединения. Указатели, массивы, структуры и объединения являются производными типами данных. Иногда в качестве производного типа в руководствах по языку Си указывают строки, однако напоминаем, что в соответствии с синтаксисом языка строковый тип в нем отсутствует. Строка определяется как частный случай одномерного массива, т.е. как массив с элементами типа char, последний элемент которого равен '\0'.

Указатели и массивы подробно рассмотрены в предыдущих главах. Эта глава посвящена структурам и объединениям. Прежде чем рассматривать их особенности и возможности, введем некоторые терминологические соглашения стандарта, которые не всегда понятны сами собой.

Данные базовых типов (int, float, ...) считаются скалярными данными. Массивы и структуры являются агрегирующими типами данных в отличие от объединений и скалярных данных, которые относятся к неагрегирующим типам. С массивами и скалярными данными мы уже хорошо знакомы, поэтому различие между агрегирующими и неагрегирующими типами поясним на примере массивов и скалярных данных базовых типов. Обычно агрегирующий тип включает несколько компонентов. Например, массив long B[12] состоит из 12 элементов, причем каждый элемент имеет тип long. Язык Си позволяет определять и одноэлементные массивы. Так, массив float A[l] состоит из одного элемента А[0]. Однако одноэлементные массивы есть слишком частный случай обыкновенных массивов со многими элементами. Итак, в общем случае массив есть агрегирующий тип данных, состоящий из набора однотипных элементов, размещенных в памяти подряд в соответствии с ростом индекса. Для агрегирующего типа данных (например, для массива) в основной памяти ЭВМ выделяется такое количество памяти, чтобы сохранять (разместить) одновременно значения всех элементов. Этим свойством массивов мы уже неоднократно пользовались. Например, следующее выражение позволяет вычислить количество элементов массива В[ ]:

sizeof(B)/sizeof(B[0])

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