Определение вида #DEFINE TES 1 приводит к макроподстановке самого простого вида - заменеимени на строку символов. Имена в #DEFINE имеют ту же самуюформу, что и идентификаторы в "с"; заменяющий текст совер-шенно произволен. Нормально заменяющим текстом является ос-тальная часть строки; длинное определение можно продолжить,поместив \ в конец продолжаемой строки. "Область действия"имени, определенного в #DEFINE, простирается от точки опре-деления до конца исходного файла. имена могут быть переопре-делены, и определения могут использовать определения, сде-ланные ранее. Внутри заключенных в кавычки строк подстановкине производятся, так что если, например, YES - определенноеимя, то в PRINTF("YES") не будет сделано никакой подстанов-ки. Так как реализация #DEFINE является частью работымаKропредпроцессора, а не собственно компилятора, имеетсяочень мало грамматических ограничений на то, что может бытьопределено. Так, например, любители алгола могут объявить #DEFINE THEN#DEFINE BEGIN {#DEFINE END ;} и затем написать IF (I > 0) THEN BEGIN A = 1; B = 2 END Имеется также возможность определения макроса с аргумен-тами, так что заменяющий текст будет зависеть от вида обра-щения к макросу. Определим, например, макрос с именем MAXследующим образом: #DEFINE MAX(A, B) ((A) > (B) ? (A) : (B)) когда строка X = MAX(P+Q, R+S); будет заменена строкой X = ((P+Q) > (R+S) ? (P+Q) : (R+S)); Такая возможность обеспечивает "функцию максимума", котораярасширяется в последовательный код, а не в обращение к функ-ции. При правильном обращении с аргументами такой макрос бу-дет работать с любыми типами данных; здесь нет необходимостив различных видах MAX для данных разных типов, как это былобы с функциями. Конечно, если вы тщательно рассмотрите приведенное вышерасширение MAX, вы заметите определенные недостатки. Выраже-ния вычисляются дважды; это плохо, если они влекут за собойпобочные эффекты, вызванные, например, обращениями к функци-ям или использованием операций увеличения. Нужно позаботить-ся о правильном использовании круглых скобок, чтобы гаранти-ровать сохранение требуемого порядка вычислений. (Рассмотри-те макрос #DEFINE SQUARE(X) X * X при обращении к ней, как SQUARE(Z+1)). Здесь возникают даженекоторые чисто лексические проблемы: между именем макро илевой круглой скобкой, открывающей список ее аргументов, недолжно быть никаких пробелов. Тем не менее аппарат макросов является весьма ценным.Один практический пример дает описываемая в главе 7 стандар-тная библиотека ввода-вывода, в которой GETCHAR и PUTCHARопределены как макросы (очевидно PUTCHAR должна иметь аргу-мент), что позволяет избежать затрат на обращение к функциипри обработке каждого символа. Другие возможности макропроцессора описаны в приложенииА. Упражнение 4-9 --------------- Определите макрос SWAP(X, Y), который обменивает значе-ниями два своих аргумента типа INT. (В этом случае поможетблочная структура).
* 5. Указатели и массивы *
Указатель - это переменная, содержащая адрес другой пе-ременной. указатели очень широко используются в языке "C".Это происходит отчасти потому, что иногда они дают единст-венную возможность выразить нужное действие, а отчасти пото-му, что они обычно ведут к более компактным и эффективнымпрограммам, чем те, которые могут быть получены другими спо-собами. Указатели обычно смешивают в одну кучу с операторамиGOTO, характеризуя их как чудесный способ написания прог-рамм, которые невозможно понять. Это безусловно спрAведливо,если указатели используются беззаботно; очень просто ввестиуказатели, которые указывают на что-то совершенно неожидан-ное. Однако, при определенной дисциплине, использование ука-зателей помогает достичь ясности и простоты. Именно этот ас-пект мы попытаемся здесь проиллюстрировать.