Или, предположим, что вы работаете с большой системой программ. Вы хотите задать макроопределение, но не уверены, использует ли ваше определение другие определения откуда-нибудь из другого места системы. В этом случае просто отмените ваше макроопределение в том месте, где оно вам больше не нужно, а оригинал этого макроопределения, если он есть, будет по-прежнему оставаться в силе для остальной части системы.
Другие упомянутые нами директивы позволяют выполнять условную компиляцию. Вот пример:
#ifdef MAVIS
#include " horse.h" /* выполнится, если MAVIS определен */
#define STABLES 5
#else
#include "cow.h" /*выполнится, если MAVIS не определен */
#define STABLES 15
#endif
Директива #ifdef сообщает, что если последующий идентификатор (MAVIS) определяется препроцессором, то выполняются все последующие директивы вплоть до первого появления #else или #endif. Когда в программе есть #else, то программа от #else до #endif будет выполняться, если идентификатор не определен.
Такая структура очень напоминает конструкцию if-else языка Си. Основная разница заключается в том, что препроцессор не распознает фигурные скобки {}, отмечающие блок, потому что он использует директивы #else (если есть) и #endif (которая должна быть) для пометки блоков директив.
Эти условные конструкции могут быть вложенными.
Директивы #ifdef и #if можно использовать с #else и #endif таким же образом. Директива #ifndef опрашивает, является ли последующий идентификатор неопределенным; эта директива противоположна #ifdef. Директива #if больше похожа на обычный оператор if языка Си. За ней следует константное выражение, которое считается истинным, если оно не равно нулю:
#if SYS == "IBM"
#include "ibm.h"
#endif
Одна из целей использования "условной компиляции" - сделать программу более мобильной. Изменяя несколько ключевых определений в начале файла, вы можете устанавливать различные значения и включать различные файлы для разных систем.
Эти краткие примеры иллюстрируют удивительную способность языка Си изощренно и строго управлять программами.
ЧТО ВЫ ДОЛЖНЫ БЫЛИ УЗНАТЬ В ЭТОЙ ГЛАВЕ
Как определять символьные константы директивой #define: #define FINGERS 10
Как включать другие файлы: #include "albanian.h"
Как определить макрофункцию: #define NEG(X) (-(X))
Когда использовать символические константы: часто.
Когда использовать макрофункции: иногда.
Опасность применения макрофункций: побочные эффекты.
ВОПРОСЫ И ОТВЕТЫ
Вопросы
1. Ниже приведены группы операторов, содержащих по одному и более макроопре делений, за которыми следуют строки исходных кодов, использующих эти макро определения. Какой результат получается в каждом случае? Правилен ли он?
a. #define FPM 5280 /* футов в миле */
dist = FPM * miles;
б. #define FEET 4
#define POD FEET + FEET
plort = FEET * POD;
в. #define SIX = 6;
nex = SIX;
г. #define NEW(X) X + 5
у = NEW(y);
berg = NEW(berg) * lob;
est = NEW(berg) / NEW(y);
nilp = lob * NEW(-berg);
2. Подправьте определение в вопросе 1.г, чтобы сделать его более надежным.
3. Определите макрофункцию, которая возвращает минимальное из двух значений.
4. Задайте макроопределение, в котором есть функция whitesp(с) считающая в программе пустые символы.
5. Определите макрофункцию, которая печатает представления значения двух целых выражений.
Ответы
1.
а. dist = 5280 * miles; правильно.
б. plot = 4 * 4 + 4; правильно, но если пользователь на самом деле хотел иметь 4 * (4 + 4), то следовало применять директиву #define POD (FEET + FEET).
в. nex = = 6; неправильно; очевидно, пользователь забыл, что он пишет для препроцессора, а не на языке Си.
г. у - у + 5; правильно.
berg = berg + 5 * lob; правильно, но, вероятно, это нежелательный результат.
est = berg + 5/у + 5; то же самое.
nilp = lob * -berg + 5; то же самое.
2. #define NEW(X) ((X) + 5)
3. #deline MIN(X,Y) ((X) < (Y) ? (X) : (Y))
4. #define WHITESP(C) ((С) == ' ' || (С) == '\n' || (С)) == '\t')
5. #define PR2(X,Y) printf(" X равно %d и Y равно %d.\n", X, Y)
Так как в этом макроопределении Х и Y никогда не используются никакими другими операциями (такими, как умножение), мы не должны ничего заключать в скобки.
УПРАЖНЕНИЕ
1. Создайте заголовочный файл определений препроцессора, которые вы хотите применять.
12. Массивы и указатели
МАССИВЫ. МНОГОМЕРНЫЕ МАССИВЫ. ИНИЦИАЛИЗАЦИЯ МАССИВОВ. УКАЗАТЕЛИ И ОПЕРАЦИИ НАД УКАЗАТЕЛЯМИ. СВЯЗЬ МЕЖДУ МАССИВОМ И УКАЗАТЕЛЕМ. ОПЕРАЦИИ & * (унарные)
Между массивами и указателями существует очень тесная связь, поэтому обычно их рассматривают вместе. Но, прежде чем исследовать эту связь, давайте проверим наши знания о массивах и пополним их, а уж после этого перейдем к изучению связи между массивами и указателями.
МАССИВЫ
Вы уже знаете, что массив представляет собой группу элементов одного типа. Когда нам требуется для работы массив, мы сообщаем об этом компилятору при помощи операторов описания. Для создания массива компилятору необходимо знать тип данных и требуемый класс памяти, т. е. то же самое, что и для простой переменной (называемой "скалярной"). Кроме того, должно быть известно, сколько элементов имеет массив. Массивы могут иметь те же типы данных и классы памяти, что и простые переменные, и к ним применим тот же принцип умолчания. Рассмотрим примеры, различных описаний массивов:
/* несколько описаний массивов */
int temp[365]; /* внешний массив из 365 целых чисел */
main( )
{
float rain[365]; /* автоматический массив из 365 чисел типа
float */
static char code[12]; /* статический массив из 12 символов */
extern temp[ ]; /* внешний массив; размер указан выше */