Поддержка JavaScript отключена
SBP-Program | |||||
На главную — > C |   | ||||
  |
Как писать макросы? Как сделать макрос? Как создавать макросы? Где найти информацию по макросам на русском? Такие вопросы — нередкость. Эта статья для начинающих, её можно было бы назвать «макросы для чайников» или «простой учебник по макросам»Прямо сейчас разберемся, как написать макрос и рассмотрим примеры макросов. Но прежде ответим на естественный вопрос: зачем нужны макросы? Для чего нужны макросы? Речь идёт о макросах в C. Макросы позволяют упрощать программу: вместо фразы можно указать её идентификатор. Есть макросы, подобные функциям, текст такой «функции» подставляется на этапе предварительной обработки в место её вызова, что потенциально повышает скорость выполнения программы, ведь нет вызова функции. Эти действия выполняются препроцессором. Есть и другие плюсы работы с макросами, но есть и минусы, о чём будет сказано ниже, когда мы перейдём к примерам создания макросов. #define в C — это директива, которая применяется при определении символических констант, идентификаторов, макрофункций. Макросы, по-английски macro, бывают двух типов: подобные функциям и подобные объектам. В нашем самоучителе по макросам мы рассмотрим работу с этими типами макросов. Пример для макроса-объекта. Это определение символической константы: Здесь определена символическая константа #define SBP «SBP-Program»
SBP — это идентификатор, Получаем: Строка #define SBP «SBP-Program»
это макроопределение. Если не указывать строку, которая подставляется вместо идентификатора, то вместо идентификатора будет подставлен пробел. Пример: Определяем идентификатор: #define IDENTIFIER_EXAMPLE
Вместо него в программе будет подставлен пробел. Получаем: Если строка-подстановка не помещается в строку файла, то применяем обратную косую черту в качестве знака переноса. Пример: Получаем: Отметим, что макросы, подобные объектам, не принимают параметры. Далее рассмотрим пример макроса-функции. Рассмотрим простой пример макроса, подобного функции, такие макросы ещё называют макрофункциями: Получаем: Строка #define mCircleLength(r) (6.28318 * (r))
это макроопределение. И что здесь определено? mCircleLength — это название макроса. Префикс m подсказывает нам, что это именно макрос, а не обычная функция. Макрос mCircleLength подобен функции, он принимает параметр r. Напомню, что макросы, подобные объектам, не принимают параметров. Что вычилсяет макрос mCircleLength? Он вычисляет длину окружности. Длина окружности равна 2Пи умножить на радиус. 2Пи = 6.28318. Препроцессор в место вызова макроса подставит наш макрос, т.е фактически строка printf будет выглядеть так: printf(«nCircle length = %.2fnn», (6.28318 * (12)));
После имени макроса в макроопределении, перед первой открывающей скобкой не должно быть пробела. Это важно. Если пробел будет, то мы получим определение символической константы. Рассмотрим тело макроса (6.28318 * (r))
оно охвачено круглыми скобками. Это существенно в общем случае. Параметр r также в скобках. И это важно. Вот примеры того, что может случиться, если пренебречь скобками: #define mSum(param) param + 2
… int nVar = 10 * mSum(5); после подстановки тела макроса получим: int nVar = 10 * 5 + 2;
nVar будет хранить число 52, а не 70, как мы ожидали. Теперь рассмотрим последствия отсутствия скобок вокруг параметра. Если в наш макрос mCircleLength будет передан параметр в виде суммы, например, 1 + 2, то получим: #define mCircleLength(r) (6.28318 * r)
… (6.28318 * 1 + 2) Вывод: скобки во многих случаях нужны. Вот пример такого макроса: #define mIntToStr(nVar) (#nVar)
в этой макрофункции строковый оператор (так его иногда называют) # подействует на параметр nVar, в результате nVar будет охвачен кавычками. Код: Получаем: оператор # охватил параметр 1 + 2 кавычками, превратив его в строковый литерал. Если убрать # в макроопределении, то макрос mIntToStr сложит 1 + 2. Пример макроса: Получаем: Строки в C можно объединить с помощью оператора ##. Пример макроса: Получаем: В макросах можно использовать указатели. Пример макроса с указателями: Получаем: Обратите внимание на расстановку скобок в теле макрофункции. В макросах можно использовать локальные переменные. Если имя такой локальной переменной совпадёт с именем глобальной, то глобальная переменная будет закрыта локальной внутри макроса. Но присвоение значений локальной переменной не повлияет на глобальную. Пример макроса: Получаем: В макросах можно применять фигурные скобки, т.о. получать блок. Пример макроса: Получаем: Возможен вариант, когда значение аргумента макроса будет вычисляться дважды. Пример такого макроса: Получаем: Почему такой результат? Инкремент ++nVar рассчитался дважды. Лучше избегать использования таких решений. В Си в конце выражения мы ставим точку с запятой int nVar = 5;
Нужно ли это делать и в макросах? Зависит от конкретного случая. Выше, в разделе «Блоки в макросах» имеется макрофункция: #define mSum(pVar) {(*(pVar)) += 10;}
Если убрать точку с запятой после 10 в этом примере (см. весь пример), то компилятор выдаст ошибку. В других случаях наличие точки с запятой ведёт к ошибке. Пример (этот пример содержит ошибку): В этом примере макрос развернётся так: if(nVar > 0)
  nVar = 10;   int nNewVar = 1; else   nVar = 0; Здесь потерялась связь между if и else. Выход следующий: #define mList do{nVar = 10; int nNewVar = 1;}while(0)
В этом случае макрос развернётся так: Последний вариант работает нормально. Код: Получаем: Макросы в C могут иметь переменное число параметров. Такие макросы называют variadic macros. Типичный пример variadic macro: Получаем: Правила здесь такие:
Теперь пример макроса с переменным числом параметров и с именованными параметрами: Получаем: Итак, если имеются именованные параметры, то переменные параметры идут последними в списке аргументов функции. Ещё есть важная деталь в последнем примере: мы не можем передать нулевое количество переменных параметров в этом случае. Почему? Потому что, если не передать ни одного переменного параметра, то после последней запятой в списке параметов будет пробел. Вот ошибочный пример: mMyPrint(szFormat);
в этом случае макрос развернётся в printf(szFormat, );
здесь после запятой нет параметра, а это ошибка в Си. Вывод: как минимум один переменный параметр должен быть передан в этом случае. Запуск макроса из другого макроса рассмотрим на простом примере: Получаем: Программирование |
  | |||
 
|
 
|
||||