160 lines
11 KiB
Markdown
160 lines
11 KiB
Markdown
|
---
|
|||
|
title: Preprocessors
|
|||
|
localeTitle: Препроцессоры
|
|||
|
---
|
|||
|
## Препроцессоры в C / CPP
|
|||
|
|
|||
|
Как видно из названия, препроцессоры - это программы, которые обрабатывают наш исходный код перед компиляцией. Существует несколько шагов, связанных с написанием программы и выполнением программы на C / C ++. Давайте посмотрим на эти шаги, прежде чем мы начнем узнавать о препроцессорах.
|
|||
|
|
|||
|
![Img](https://i.imgur.com/Pb0aTkV.png)
|
|||
|
|
|||
|
Вы можете увидеть промежуточные шаги на приведенной выше диаграмме. Исходный код, написанный программистами, хранится в файле program.c. Затем этот файл обрабатывается препроцессорами, а файл расширенного исходного кода создается с именем program. Этот расширенный файл компилируется компилятором и создается файл объектного кода с именем program.obj. Наконец, компоновщик связывает этот файл объектного кода с объектным кодом библиотечных функций для генерации исполняемого файла program.exe.
|
|||
|
|
|||
|
Препроцессорные программы предоставляют директивы препроцессоров, которые сообщают компилятору предварительно обработать исходный код перед компиляцией. Все эти директивы препроцессора начинаются с символа `#` (хеш). Этот символ ('#') в начале инструкции в программе C / C ++ указывает, что это предпроцессорная директива. Мы можем разместить эти предпроцессорные директивы в любой нашей программе. Примерами некоторых препроцессорных директив являются: `#include` , `#define` , `#ifndef` и т. Д.
|
|||
|
|
|||
|
### Типы предпроцессорных директив:
|
|||
|
|
|||
|
1. макрос
|
|||
|
2. Включение файлов
|
|||
|
3. Условная компиляция
|
|||
|
4. Другие директивы
|
|||
|
|
|||
|
### Макросы:
|
|||
|
|
|||
|
Макросы являются частью кода в программе, которому дано какое-то имя. Всякий раз, когда это имя встречается компилятором, компилятор заменяет это имя фактическим фрагментом кода. Директива `#define` используется для определения макроса.
|
|||
|
|
|||
|
```cpp
|
|||
|
#include<iostream>
|
|||
|
#define LIMIT 3
|
|||
|
int main()
|
|||
|
{
|
|||
|
for(int i=0; i < LIMIT; i++)
|
|||
|
{
|
|||
|
std::cout<<i<<" " ;
|
|||
|
}
|
|||
|
return 0;
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
Вывод:
|
|||
|
|
|||
|
`0 1 2`
|
|||
|
|
|||
|
В вышеприведенной программе, когда компилятор выполняет слово `LIMIT` он заменяет его на 3. Слово `LIMIT` в определении макроса называется макромодом, а «3» - расширением макроса.
|
|||
|
|
|||
|
В конце определения макроса не должно быть запятой (';'). Определения макросов не нуждаются в полуточке.
|
|||
|
|
|||
|
### Включение файла:
|
|||
|
|
|||
|
Этот тип предпроцессорной директивы сообщает компилятору включить файл в программу исходного кода. Существует два типа файлов, которые могут быть включены пользователем в программу:
|
|||
|
|
|||
|
* \#### Файл заголовка или стандартные файлы: Эти файлы содержат определение предопределенных функций, таких как printf (), ... scanf () и т. Д. Эти файлы должны быть включены для работы с этими функциями. ... Различные функции объявляются в разных файлах заголовков. Например ... стандартные операции ввода-вывода находятся в файле «iostream», тогда как функции, которые ... выполняют строковые операции, находятся в файле «string».
|
|||
|
|
|||
|
#### Синтаксис:
|
|||
|
|
|||
|
`#include< file_name >` где file\_name - это имя файла, который будет включен. Скобки `<` и `>` сообщают компилятору искать файл в стандартном каталоге.
|
|||
|
|
|||
|
* \#### Пользовательские файлы: Когда программа становится очень большой, целесообразно разделить ее на более мелкие файлы и включать в случае необходимости. Эти типы файлов являются определяемыми пользователем файлами. Эти файлы могут быть включены как: ... `#include"filename"`
|
|||
|
|
|||
|
### Условная компиляция:
|
|||
|
|
|||
|
Условные директивы компиляции - это типы директив, которые помогают скомпилировать определенную часть программы или пропустить компиляцию некоторой определенной части программы на основе некоторых условий. Это можно сделать с помощью двух команд предварительной обработки `ifdef` и `endif` .
|
|||
|
|
|||
|
#### Синтаксис:
|
|||
|
|
|||
|
```cpp
|
|||
|
ifdef macro_name
|
|||
|
statement1;
|
|||
|
statement2;
|
|||
|
statement3;
|
|||
|
.
|
|||
|
.
|
|||
|
.
|
|||
|
statementN;
|
|||
|
endif
|
|||
|
```
|
|||
|
|
|||
|
Если макрос с именем «macroname» определен, тогда блок операторов будет выполняться нормально, но если он не определен, компилятор просто пропустит этот блок операторов.
|
|||
|
|
|||
|
### Другие директивы:
|
|||
|
|
|||
|
Помимо вышеуказанных директив есть еще две директивы, которые обычно не используются. Это:
|
|||
|
|
|||
|
1. \##### `#undef` Директива: Директива `#undef` используется для определения существующего макроса. Эта директива работает как:
|
|||
|
|
|||
|
##### Синтаксис:
|
|||
|
|
|||
|
`#undef LIMIT` Использование этого оператора будет определять неопределенный существующий макрос LIMIT. После этого утверждения каждый оператор `#ifdef LIMIT` будет оценивать значение false.
|
|||
|
|
|||
|
2. \##### # `#pragma` директива: Эта директива является директивой специального назначения и используется для включения или отключения некоторых функций. Этот тип директив специфичен для компилятора, т. Е. Они варьируются от компилятора к компилятору. Некоторые из директив `#pragma` обсуждаются ниже:
|
|||
|
|
|||
|
##### `#pragma startup` и `#pragma exit` :
|
|||
|
|
|||
|
Эти директивы помогают нам определить функции, которые необходимы для запуска до запуска программы (до того, как элемент управления переходит к main ()) и непосредственно перед выходом программы (как раз перед возвратом элемента управления из main ()).
|
|||
|
|
|||
|
```cpp
|
|||
|
#include<stdio.h>
|
|||
|
void func1();
|
|||
|
void func2();
|
|||
|
#pragma startup func1
|
|||
|
#pragma exit func2
|
|||
|
void func1()
|
|||
|
{
|
|||
|
printf("Inside func1() ");
|
|||
|
}
|
|||
|
void func2()
|
|||
|
{
|
|||
|
printf("Inside func2() ");
|
|||
|
}
|
|||
|
int main()
|
|||
|
{
|
|||
|
printf("Inside main() ");
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
Вывод:
|
|||
|
`Inside func1() Inside main() Inside func2()`
|
|||
|
Вышеприведенный код будет выдавать результат, указанный ниже при запуске на компиляторах GCC:
|
|||
|
Вывод:
|
|||
|
`Inside main()`
|
|||
|
Это происходит потому, что GCC не поддерживает запуск #pragma или выход. Однако вы можете использовать приведенный ниже код для аналогичного вывода на компиляторах GCC.
|
|||
|
|
|||
|
```cpp
|
|||
|
#include<stdio.h>
|
|||
|
void func1();
|
|||
|
void func2();
|
|||
|
void __attribute__((constructor)) func1();
|
|||
|
void __attribute__((destructor)) func2();
|
|||
|
void func1()
|
|||
|
{
|
|||
|
printf("Inside func1()\n");
|
|||
|
}
|
|||
|
void func2()
|
|||
|
{
|
|||
|
printf("Inside func2()\n");
|
|||
|
}
|
|||
|
int main()
|
|||
|
{
|
|||
|
printf("Inside main()\n");
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
##### `#pragma warn` Директива:
|
|||
|
|
|||
|
Эта директива используется, чтобы скрыть предупреждающее сообщение, которое отображается во время компиляции. Мы можем скрыть предупреждения, как показано ниже:
|
|||
|
|
|||
|
##### `#pragma warn -rvl` :
|
|||
|
|
|||
|
Эта директива скрывает предупреждение, которое возникает, когда функция, которая должна возвращать значение, не возвращает значение.
|
|||
|
|
|||
|
##### `#pragma warn -par` :
|
|||
|
|
|||
|
Эта директива скрывает предупреждение, которое возникает, когда функция не использует переданные ей параметры.
|
|||
|
|
|||
|
##### `#pragma warn -rch` :
|
|||
|
|
|||
|
Эта директива скрывает предупреждение, которое возникает, когда код недостижим. Например: любой код, написанный после оператора return в функции, недоступен.
|