Введение
Препроцессор, как утверждает Википедия – это компьютерная программа, принимающая данные на входе и выдающая данные, предназначенные для входа другой программы (например, компилятора). Чаще всего он применяется для автоматизации какого-либо процесса в программировании, например, подсчёта переменных и т.п. В Pawn препроцессор используется непосредственно для формирования какого-то структурированного кода, анализа чего-либо и т.д.
Определения и их эквиваленты
Определения (в данном случае имеется в виду object-like macro) – это константы, которые заменяются значениями в момент
прекомпиляции. Они имеют глобальную зону видимости.
PHP код:
#define SOME_CONSTANT (0xFFFFFFFF)
Эквивалент определений – оператор const. Есть два основных отличия: оператор может иметь локальную зону видимости;
заменяется на этапе компиляции.
(!) Следует отметить, что, как и у определения, так и у оператора const имеется ограничение на количество символов в названии – 31 символ.
Макросы
Макросы (в данном случая имеется в виду function-like macro, т.е. функция-подобный макрос) – важнейшая часть препроцессора, которая может обрабатывать полученные пользовательские данные и распоряжаться ими. В программировании макросы используются довольно часто, это обусловлено тем, что в программировании очень много одинаковых операций.
Синтаксис
Синтаксис макроса стоит из нескольких частей:
Код:
#define <название и его содержимое><конец макроса> <значение для переопределения>
Примечание:
- От начала названия и до конца макроса не должно быть ни единого пробела, иначе будет ошибка. Однако, чтобы парсить пробелы необходимо использовать ascii-идентификаторы символов. Например, ascii-код для пробела – '\32;'
- Первый символа макроса должен состоять из букв латинского алфавита или знаков ‘_’, ‘@’. Последующие же символы могут использовать и цифры.
- В третьей же части использовать пробелы можно.
Параметры
Параметры являются неотъемлемой частью макрофункций, ведь с помощью них передаётся вся пользовательская информация.
Параметры в макрофункциях задаются с помощью процента и номера аргумента.
Код:
#define macrofunc(%0,%1) %0 %1
Опасные моменты использования параметров
Наблюдая за тем, что делают люди с помощью макросов, замечаю, что некоторые даже не подозревают о неработоспособности своих творений в некоторых ситуациях.
Например, ситуация с арифметическими операциями в параметрах макроса.
Перейдём к рассмотрению примера:
PHP код:
#define macrofunc(%0) (8 * %0)
main()
{
printf("%d", macrofunc(5 + 5));
}
В выводе ожидаем 80, а получаем 45. Дело в том, что макрос просто подставляет код туда, куда указанно. Он не определяет последовательность операций и т.п.
Для того чтобы избежать данную проблему необходимо использовать круглые скобки, которые говорят о том, что начальной операцией является выражение в скобках.
PHP код:
#define macrofunc(%0) (8 * (%0))
main()
{
printf("%d", macrofunc(5 + 5));
}
Теперь все правильно. Итак, давайте выведем несколько правил, которые помогут избежать выше указанную проблему:
- Скобки вокруг всего выражения;
- Скобки вокруг каждого параметра;
Однако, все выше перечисленные правила не помогут в случае, если вы используете инкрементацию/декрементацию в параметре.
Рассмотрим пример:
PHP код:
#define macrofunc(%0) ((%0) * (%0))
main()
{
new x;
printf("%d", macrofunc(++x));
}
В данном случаем стоит, вообще, отказаться от макросов в пользу функций.
Переход на новую строку
Для читаемости макрофункций используется обратный слеш, которые позволяет определить переход к новой строчке. Кроме того, это работает практически для каждой препроцессорной директивы.
Перейдём к примеру:
PHP код:
#define SOME_MACRO(%0) \
forward SOME_%0(); \
public SOME_%0();
Также мы можем сделать и при обращении к макрофункциям:
PHP код:
#define SOME_MACRO_FUNC(%0,%1) \
(%0 + %1)
main()
{
SOME_MACRO_FUNC(0, \
8 \
);
}
Другие препроцессорные директивы
#include и #tryinclude
Данные директивы вставляют код из указанного файла в исходный. Т.е. подключив какой-либо файл, мы можем пользоваться его содержимым: функциями, глобальными переменными и т.п.
Главным отличием #include и #tryinclude является то, что последний не выводит ошибок, если файл не был загружен.
#assert
Проверяет указанное выражение. Если выражение возвращает нуль, то выдаёт ошибку.
#if, #endif, #elseif, #else
Начнём с того, что у каждой директивы #if должна быть соответствующая директива #endif. Кроме того, между данными препроцессорными директивами может располагаться любое количество директив #elseif. Однако, допускается не более одной директивы #else.
PHP код:
#define SOME_CONSTANT (0xFFFFFFFF)
#if SOME_CONSTANT == 0
#define SOME_CONSTANT_1 0
#elseif SOME_CONSTANT > 0
#define SOME_CONSTANT_1 1
#elseif SOME_CONSTANT < 0
#define SOME_CONSTANT_1 2
#else
#error Opss
#endif
#endinput
Данная препроцессорная директива указывает на то, что компилятор не должен читать последующий код, т.е. игнорировать его.
PHP код:
#if defined _PRO_PAWN_RU
#endinput
#endif
#define _PRO_PAWN_RU
#error
Директива выдаёт ошибку с указанным текстом при компиляции.
PHP код:
#error Some text
#undef
Удаляет макрофункцию или константу. Синтаксис данной директивы следующий:
Строки
В препроцессоре существует оператор “#” помещающий значение в двойные кавычки. Например, рассмотрим код:
PHP код:
#define TO_STR(%0) (#%0)
main()
{
printf("%s", TO_STR(pro-pawn.ru));
}
Данный код выведет "pro-pawn.ru" в консоль.
Вывод
Несмотря на то, что препроцессор является хорошим инструментом, его стоит опасаться. Существует немалое количество ошибок, которые могут изменить логику вашего скрипта. Используйте препроцессор аккуратно.
Если у вас появились какие-то вопросы, или вы думаете, что я что-то забыл, напишите об этом.
Автор: VVWVV
Исключительно для pro-pawn.ru
Копирование данной статьи на других ресурсах без разрешения автора запрещено!