Иначе говоря, переменная cc
могла бы легко получить другое значение, которого вы не ожидали, исходя из определения макроса. Определяя макрос, не забывайте заключить в скобки каждый аргумент, входящий в выражение.
aa++
, а поскольку переменная x в макросе MAX
используется дважды, переменная a может инкрементироваться также дважды. Не передавайте макросу аргументы, имеющие побочные эффекты.
Какой-то “гений” определил макрос следующим образом и поместил его в широко используемый заголовочный файл. К сожалению, он также назвал его max
, а не MAX
, поэтому когда в стандартном заголовке языка C++ объявляется функция
template
имя max
разворачивается с аргументами T a
и T b
, и компилятор видит строку
template
{ return a
Сообщения об ошибке, выдаваемые компилятором, интересны, но не слишком информативны. В случае опасности можете отменить определение макроса.
#undef max
К счастью, этот макрос не привел к большим неприятностям. Тем не менее в широко используемых заголовочных файлах существуют десятки тысяч макросов; вы не можете отменить их все, не вызвав хаоса.
Не все параметры макросов используются как выражения. Рассмотрим следующий пример:
#define ALLOC(T,n) ((T*)malloc(sizeof(T)*n))
Это реальный пример, который может оказаться очень полезным для предотвращения ошибок, возникающих из-за согласованности между желательным типом выделяемой памяти и использованием оператора sizeof
.
double* p = malloc(sizeof(int)*10); /* похоже на ошибку */
К сожалению, написать макрос, который позволял бы выявить исчерпание памяти, — нетривиальная задача. Это можно было бы сделать, если бы мы в каком-то месте программы соответствующим образом определили переменную error_var
и функцию error()
.
#define ALLOC(T,n) (error_var = (T*)malloc(sizeof(T)*n), \
(error_var==0)\
?(error("Отказ выделения памяти"),0)\
:error_var)
Строки, завершающиеся символом \
, не содержат опечаток; это просто способ разбить определение макроса на несколько строк. Когда мы пишем программы на языке C++, то предпочитаем использовать оператор new
.
27.8.2. Синтаксис макросов
Можно определить макрос, который приводит текст исходного кода в приятный для вас вид. Рассмотрим пример.
#define forever for(;;)
#define CASE break; case
#define begin {
#define end }
• Многие люди не разделяют ваших взглядов на то, что считать лучшим синтаксисом.
• Улучшенный синтаксис является нестандартным и неожиданным; остальные люди будут сбиты с толку.
• Использование улучшенного синтаксиса может вызвать непонятные ошибки компиляции.
• Текст программы, который вы видите перед собой, не совпадает с текстом, который видит компилятор, и компилятор сообщает об ошибках, используя свой словарный запас, а не ваш.
Не пишите синтаксические макросы, для того чтобы улучшить внешний вид вашего кода. Вы и ваши лучшие друзья могут считать его превосходным, но опыт показывает, что вы окажетесь в крошечном меньшинстве среди более крупного сообщества программистов, поэтому кому-то придется переписать ваш код (если он сможет просуществовать до этого момента).
27.8.3. Условная компиляция
Представьте себе, что у вас есть два варианта заголовочного файла, например, один — для операционной системы Linux, а другой — для операционной системы Windows. Как выбрать правильный вариант в вашей программе? Вот как выглядит общепринятое решение этой задачи:
#ifdef WINDOWS
#include "my_windows_header.h"
#else
#include "my_linux_header.h"
#endif
Теперь, если кто-нибудь уже определил WINDOWS
до того, как компилятор увидел этот код, произойдет следующее:
#include "my_windows_header.h"
В противном случае будет включен другой заголовочный файл.
#include "my_linux_header.h"
Директива #ifdef WINDOWS
не интересуется, что собой представляет макрос WINDOWS
; она просто проверяет, был ли он определен раньше.
В большинстве крупных систем (включая все версии операционных систем) существуют макросы, поэтому вы можете их проверить. Например, можете проверить, как компилируется ваша программа: как программа на языке C++ или программа на языке C.
#ifdef __cplusplus