#define WRONG (X, Y) #Х # VA_ARGS #у // не работает
Выбор между макросом и функцией
Многие задачи могут быть решены за счет применения макроса с аргументами либо функции. Что должно использоваться? Здесь нет каких-то строго определенных правил, но есть ряд соображений, которые следует принимать во внимание.
Макросы несколько сложнее в применении, чем обычные функции, т.к. макросы могут иметь неожиданные побочные эффекты, если вы проявите неосмотрительность. Некоторые компиляторы ограничивают определение макроса одной строкой, и вероятно лучше придерживаться этого ограничения, даже если в вашем компиляторе оно отсутствует.
Выбор между макросом и функцией связан с достижением компромисса между быстродействием и размером кода. Макрос генерирует встраиваемый код, т.е. в программу помещается оператор. Если макрос используется 20 раз, в программу вставляется 20 строк кода. Когда 20 раз применяется функция, в программе все равно содержится только одна копия ее операторов, что уменьшает размер кода. С другой стороны, поток управления программы должен переходить туда, где находится функция, и затем возвращаться в место ее вызова. Этот процесс отнимает больше времени, чем выполнение встраиваемого кода.
Преимущество макросов в том, что они не заботятся о типах переменных. (Причина связана с тем, что они имеют дело со строками символов, а не действительными значениями.) Таким образом, макрос SQUARE (х) может с одинаковым успехом использоваться с типом int или float.
В С99 появилась третья альтернатива — встраиваемые функции. Мы обсудим их позже в этой главе. Программисты обычно применяют макросы для простых функций, таких как перечисленные ниже:
#define MAX(X,Y) ((X) > (Y) ? (X) : (Y) )
#define ABS(X) ( (X) < 0 ? -(X) : (X))
#define ISSIGN(X) ((X) == ' + ' || (X) == '-' ? 1 : 0)
(Последний макрос имеет значение 1, или истинное, если х является символом алгебраического знака.)
Далее указано несколько моментов, о которых не следует забывать.
• Помните, что имя макроса не должно содержать пробелов, но пробелы допускаются в замещающей строке. В ANSI С разрешены пробелы в списке аргументов.
• Заключайте в скобки каждый аргумент и определение в целом. Это гарантирует корректное группирование элементов в выражении следующего рода:
forks = 2 * MAXtguests + 3, last);
• Используйте прописные буквы для имен функциональных макросов. Данное соглашение не так широко распространено, как применение прописных букв в именах константных макросов. Тем не менее, одна из веских причин использования прописных букв связана с тем, что это напоминает вам о возможных побочных эффектах макросов.
глава 16
• Если вы намерены применять макрос вместо функции главным образом для ускорения работы программы, сначала попытайтесь выяснить, обеспечит ли это заметный выигрыш. Макрос, который используется в программе один раз, не приведет к значительному улучшению скорости ее выполнения. Макрос, находящийся внутри вложенного цикла, является намного лучшим кандидатом для ускорения работы программы. Многие системы предлагают профилировщики программ, которые помогают выявлять фрагменты кода, требующие наибольшего времени выполнения.
Предположим, что вы разработали несколько нужных вам функциональных макросов. Должны ли вы набирать их каждый раз, когда пишется новая программа? Нет, если вы будете помнить о директиве # include, которая рассматривается в следующем разделе.
Включение файлов: директива #±nciude
Когда препроцессор встречает директиву #include, он ищет файл с указанным в директиве именем и включает его содержимое в текущий файл. Директива #include в файле исходного кода заменяется текстом включаемого файла. Это аналогично вводу содержимого включаемого файла в той же позиции внутри исходного файла. Существуют две разновидности # include:
#include <-- Имя файла указано в угловых скобках
#include "mystuff.h" <���— Имя файла указано в двойных кавычках
В системе Unix угловые скобки сообщают препроцессору о необходимости поиска файла в одном или большем числе стандартных системных каталогов. Двойные кавычки говорят о том, что сначала следует просмотреть текущий каталог (или другой каталог, который указан вместе с именем файла), а затем искать в стандартных каталогах:
#include <���— Поиск в системных каталогах
#include "hot.h" <���— Поиск в текущем рабочем каталоге
#include "/usr/biff/p.h" <- Поиск в каталоге/usr/biff
Интегрированные среды разработки (IDE — Integrated Development Environment) также имеют стандартное местоположение или несколько таких местоположений для системных заголовочных файлов. Многие IDE-среды предоставляют опции меню для указания дополнительных местоположений, которые должны просматриваться в случае применения угловых скобок. Как и в Unix, использование двойных кавычек означает поиск сначала в локальном каталоге, но что это в точности за каталог — зависит от компилятора. Некоторые компиляторы ищут в том же каталоге, где находится исходный код, другие — в текущем рабочем каталоге, а третьи — в каталоге, содержащем файл проекта.
Читать дальше