Последнее обновление:2026-10-12
C++ макросы и условная компиляция¶
Об авторе:¶
Личный официальный аккаунт:

1.Специальные символы в макросах¶
Они бывают нескольких видов: #, ##, \
1.1 Оператор строкования(#)¶
Если перед параметром в макросе использовать один #, препроцессор преобразует этот параметр в массив символов; иначе говоря: # означает «строкование»; # в определении макроса преобразует параметр, следующий за ним, в строку.
Обратите внимание: его можно применять только в определениях макросов с передаваемыми параметрами, и он должен стоять перед именем параметра в теле макроса.
Например:
#define exp(s) printf("test s is:%s\n",s)
#define exp1(s) printf("test s is:%s\n",#s)
#define exp2(s) #s
int main() {
exp("hello");
exp1(hello);
string str = exp2( bac );
cout<<str<<" "<<str.size()<<endl;
/**
* 忽略传入参数名前面和后面的空格。
*/
string str1 = exp2( asda bac );
/**
* 当传入参数名间存在空格时,编译器将会自动连接各个子字符串,
* 用每个子字符串之间以一个空格连接,忽略剩余空格。
*/
cout<<str1<<" "<<str1.size()<<endl;
return 0;
}
Приведённый код демонстрирует базовое использование и правила обработки пробелов; правила обработки пробелов таковы:
- Пробелы перед именем переданного параметра и после него игнорируются.
Вывод:
- Если между частями переданного имени параметра есть пробелы, компилятор автоматически соединит подстроки, разделяя их одним пробелом и игнорируя остальные пробелы.
Вывод:
1.2 Оператор соединения символов(##)¶
«##» — это способ соединения через разделение: он сначала разделяет, а затем выполняет принудительное соединение. Он превращает несколько формальных параметров макроса в одно имя фактического параметра.
Замечания:
(1)При соединении формальных параметров через ## пробелы до и после ## необязательны.
(2)Полученное после соединения имя фактического параметра должно быть реально существующим именем параметра либо известным компилятору определением макроса.
(3)Если параметр после ## сам является макросом, ## помешает его раскрытию.
Пример:
#define expA(s) printf("前缀加上后的字符串为:%s\n",gc_##s) //gc_s必须存在
// 注意事项2
#define expB(s) printf("前缀加上后的字符串为:%s\n",gc_ ## s) //gc_s必须存在
// 注意事项1
#define gc_hello1 "I am gc_hello1"
int main() {
// 注意事项1
const char * gc_hello = "I am gc_hello";
expA(hello);
expB(hello1);
}
1.3 Оператор продолжения строки(\)¶
Когда определяемый макрос невозможно уместить в одну строку, можно использовать «\», чтобы продолжить определение макроса на следующей строке.
Обратите внимание: оставляйте пробел перед .
Приведённый код см. в: sig_examp.cpp
2.Использование do{...}while(0)¶
2.1 Избежание искажения смысла¶
Например:
После раскрытия этот макрос превращается в:
Задумывалось, что при a>0 выполняются f1 и f2, а на самом деле f2 выполняется каждый раз — вот и ошибка.
Чтобы решить такую проблему, при написании кода обычно можно использовать блок {}.
Например:
Но при раскрытии такого макроса появляется лишняя точка с запятой, что не совсем правильно синтаксически. (Хотя компиляция и выполнение проходят без проблем, в норме точки с запятой нет.)
2.2Избежание управления потоком с помощью goto¶
В некоторых функциях нам может понадобиться выполнить очистку перед оператором return — например, освободить память, выделенную через malloc в начале функции; goto всегда был простым способом сделать это:
int f() {
int *p = (int *)malloc(sizeof(int));
*p = 10;
cout<<*p<<endl;
#ifndef DEBUG
int error=1;
#endif
if(error)
goto END;
// dosomething
END:
cout<<"free"<<endl;
free(p);
return 0;
}
Но поскольку goto не соответствует структурности программной инженерии и может сделать код труднопонимаемым, многие не рекомендуют его использовать. В этом случае для тех же целей можно применить do{...}while(0):
int ff() {
int *p = (int *)malloc(sizeof(int));
*p = 10;
cout<<*p<<endl;
do{
#ifndef DEBUG
int error=1;
#endif
if(error)
break;
//dosomething
}while(0);
cout<<"free"<<endl;
free(p);
return 0;
}
Здесь основная часть функции обёрнута в do{...}while(0), break используется вместо goto, а последующая очистка выполняется после while; так мы достигаем того же эффекта, а читаемость и сопровождаемость кода намного лучше, чем у варианта с goto.
2.3 Избежание предупреждений, вызванных макросами¶
Из-за ограничений разных архитектур в ядре часто используются пустые макросы. При компиляции такие пустые макросы выдают warning; чтобы избежать таких предупреждений, можно определять пустые макросы через do{...}while(0):
2.4 Определение единого блока функции для сложных операций¶
Если у вас есть сложная функция со множеством переменных и вы не хотите добавлять новую функцию, можно использовать do{...}while(0) и писать свой код внутри него: там можно определять переменные, не беспокоясь, что их имена совпадут с переменными до или после функции. Подразумевается случай, когда одна переменная используется в нескольких местах (причём смысл в каждом месте разный); мы можем сузить область видимости в каждом do-while, например:
int fc()
{
int k1 = 10;
cout<<k1<<endl;
do{
int k1 = 100;
cout<<k1<<endl;
}while(0);
cout<<k1<<endl;
}
Приведённый код см. в: do_while.cpp
Учебная статья: https://www.cnblogs.com/lizhenghn/p/3674430.html
Похожие темы¶
- C++ чисто виртуальные функции и абстрактные классы | C++那些事
- C++ утверждения assert | C++那些事
- C++ битовые поля | C++那些事
- Реализация полиморфизма C++ на языке C | C++那些事
- C++ константы const и его модификация | C++那些事
- C++ всё семейство const | C++那些事
- C++ оператор разыменования * | C++那些事
- C++ инициализация и присваивание | C++那些事
- C++ возврат нескольких значений | C++那些事
- C++ ключевое слово mutable | C++那些事