Директивы препроцессора C#
Хотя у компилятора нет отдельного препроцессора, директивы, описанные в этом разделе, обрабатываются так, как если бы он был. Они используются в условной компиляции. В отличие от директив C и C++ вы не можете использовать их для создания макросов. Директива препроцессора должна быть единственной инструкцией в строке.
Контекст, допускающий значение NULL
Директива препроцессора #nullable устанавливает контекст с заметками о допустимости значений NULL и контекст с предупреждениями о допустимости значений NULL. Эта директива определяет, действуют ли заметки, допускающие значение NULL, и могут ли быть заданы предупреждения о допустимости значений NULL. Каждый контекст либо отключен, либо включен.
Оба контекста можно указать на уровне проекта (за пределами исходного кода C#). Директива #nullable управляет контекстами заметок и предупреждений и имеет приоритет над параметрами уровня проекта. Директива задает контексты, которыми управляет, пока другая директива не переопределит ее, или до конца исходного файла.
Ниже приведены результаты использования директив:
Условная компиляция
Для управления условной компиляцией используются четыре директивы препроцессора.
Для традиционных проектов, в которых не используется пакет SDK, необходимо вручную настроить символы условной компиляции для различных целевых платформ в Visual Studio с помощью страниц свойств проекта.
В следующем примере показано, как тестировать разные целевые платформы для использования более новых интерфейсов API, когда это возможно:
Определение символов
Используйте следующие две директивы препроцессора, чтобы определить или отменить определение символов для условной компиляции.
Директиву #define нельзя использовать для объявления значений констант, как это обычно делается в C и C++. Для определения констант в C# следует использовать статические элементы класса или структуры. При наличии нескольких констант имеет смысл создать для них отдельный класс «Constants».
Определение областей
Вы можете определить области кода, которые можно свернуть в структуру, используя следующие две директивы препроцессора.
Директива #region позволяет указать блок кода, который можно разворачивать и сворачивать с помощью функции структурирования в редакторе кода. В больших файлах кода удобно сворачивать или скрывать одну область или несколько, чтобы не отвлекаться от той части файла, над которой в настоящее время идет работа. В следующем примере показано, как определить область:
Сведения об ошибках и предупреждениях
Вы указываете компилятору создавать определенные пользователем ошибки и предупреждения компилятора, а также управлять сведениями о строках с помощью следующих директив.
#error позволяет создать определяемую пользователем ошибку CS1029 из определенного места в коде. Пример:
Компилятор обрабатывает #error version особым образом и сообщает об ошибке компилятора CS8304 с сообщением, содержащим используемые версии компилятора и языка.
#warning позволяет создать предупреждение компилятора CS1030 первого уровня из определенного места в коде. Пример:
Директива #line позволяет изменять номер строки компилятора и при необходимости имя файла, в который будут выводиться ошибки и предупреждения.
В следующем примере показано, как включить в отчет два предупреждения, связанные с номерами строк. Директива #line 200 принудительно устанавливает номер следующей строки 200 (по умолчанию используется номер 6). До выполнения следующей директивы #line в отчете будет указываться имя файла Special. Директива #line default по умолчанию восстанавливает нумерацию строк в исходное состояние с учетом строк, номера которых были изменены с помощью предшествующей директивы.
В результате компиляции формируются следующие результаты:
Директива #line hidden скрывает последующие строки для отладчика. В этом случае при пошаговой проверке кода разработчиком все строки между #line hidden и следующей директивой #line (кроме случаев, когда это также директива #line hidden ) будут пропущены. Этот параметр также можно использовать для того, чтобы дать ASP.NET возможность различать определяемый пользователем и создаваемый компьютером код. В основном эта функция используется в ASP.NET, но также может быть полезна и в других генераторах исходного кода.
Директива #line hidden не влияет на имена файлов и номера строк в отчетах об ошибках. Это значит, что при обнаружении ошибки в скрытом блоке компилятор укажет в отчете текущие имя файла и номер строки, где найдена ошибка.
Директива #line filename задает имя файла, которое будет отображаться в выходных данных компилятора. По умолчанию используется фактическое имя файла с исходным кодом. Имя файла должно заключаться в двойные кавычки (» «). Перед ним должен указываться номер строки.
Начиная с C# 10 можно использовать новую форму директивы #line :
Компоненты этой формы:
В предыдущем примере будет создано следующее предупреждение:
После повторного сопоставления переменная b находится в первой строке, в шестом символе.
Предметно-ориентированные языки (DSL) обычно используют этот формат, чтобы обеспечить более эффективное сопоставление исходного файла с созданными выходными данными C#. Дополнительные примеры этого формата см. в разделе примеров в спецификации функции.
Директивы pragma
Директива #pragma предоставляет компилятору специальные инструкции для компиляции файла, в котором она появляется. Компилятор должен поддерживать эти инструкции. Другими словами, директиву #pragma невозможно использовать для создания настраиваемых инструкций предварительной обработки.
pragma-name — имя распознанной прагмы, а pragma-arguments — аргументы, относящиеся к прагме.
#pragma warning
#pragma warning может включать или отключать определенные предупреждения.
warning-list — список номеров предупреждений с разделителем-запятой. Префикс CS является необязательным. Если номера предупреждений не указаны, disable отключает все предупреждения, а restore включает все предупреждения.
Чтобы найти номера предупреждений в Visual Studio, выполните сборку проекта, а затем поиск номеров предупреждений в окне Вывод.
#pragma checksum
Создает контрольные суммы для исходных файлов, чтобы помочь с отладкой страниц ASP.NET.
«filename» — это имя файла, для которого требуется наблюдение за изменениями или обновлениями, «
Отладчик Visual Studio использует контрольную сумму, чтобы подтвердить нахождение правильного источника. Компилятор вычисляет контрольную сумму для исходного файла, а затем передает результат в файл базы данных (PDB) программы. Отладчик затем использует PDB-файл для сравнения с контрольной суммой, вычисленной им для исходного файла.
Это решение не работает для проектов ASP.NET, так как рассчитанная контрольная сумма относится к созданному исходному файлу, а не файлу ASPX. Чтобы решить эту проблему, #pragma checksum предоставляет поддержку контрольных сумм для страниц ASP.NET.
При создании проекта ASP.NET в Visual C# созданный исходный файл содержит контрольную сумму для ASPX-файла, из которого создается источник. Затем компилятор записывает эти данные в PDB-файл.
Если компилятор не обнаруживает директиву #pragma checksum в файле, он вычисляет контрольную сумму и записывает значение в PDB-файл.
Си++/Препроцессорные директивы
Препроцессор входит в любой компилятор программ на Си++ и любую среду разработки, рассчитаную на этот язык. Препроцессор обрабатывает исходный код программ до их компиляции. Препроцессорные команды, или директивы, управляют работой препроцессора.
Таких команд немного, они все начинаются со знака решётки ( # ) и должны быть в начале строки исходного кода:
#define эта директива предусматривает определение макросов или препроцессорных идентификаторов, простейшее применение это замены в тексте программы. #include позволяет включать текст других файлов в текст вашей программы. #undef отменяет действие директивы #define #if организация условной обработки директив. #ifdef организация условной обработки директивю #else организация условной обработки директив. #endif организация условной обработки директив. #elif организация условной обработки директив. #line управление нумерацией строк в тексте программы. #error задаёт текст диагностического сообщения, выводящиеся при наличии ошибок. #pragma зависит от среды разработки. # нулевая, или пустая, директива, бездейственно пропускается.
Директива #define [ править ]
Директива #define служит для замены часто использующихся констант, ключевых слов, операторов или выражений некоторыми идентификаторами. Идентификаторы, заменяющие текстовые или числовые константы, называют именованными константами. Идентификаторы, заменяющие фрагменты программ, называют макроопределениями, причём макроопределения могут иметь аргументы.
Основная форма синтаксиса директивы #define :
Так например, в программе
Переменная а примет значение 5.
Директива #include [ править ]
Директива #include добавляет содержимое заданного файла в другой файл. Можно организовать определения констант и макро в отдельном файле, а затем вставить его директивой #include в любой другой файл. Вставка файлов также очень удобна для объединения объявлений внешних переменных и сложных типов данных. Нужно определить и задать имена этих типов только один раз в созданный для этих целей файл.
Директива #include информирует препроцессор о том, что содержание файла с заданным именем следует обрабатывать так, как будто оно присутствует в исходной программе в месте расположения этой директивы. Новый текст также может содержать директивы препроцессора. Препроцессор выполняет директивы в новом тексте, а затем продолжает обработку текста исходного файла.
Спецификация пути это имя файла, которому может предшествовать имя каталога. Это должно быть имя существующего файла. Синтаксис спецификации файла зависит от операционной системы, в которой компилируется программа.
При поиске файлов препроцессор использует концепцию «стандартного» каталога. Расположение стандартных каталогов для файлов зависит от реализации и операционной системы. Определение стандартного каталога можно найти в руководстве по компилятору.
Препроцессор останавливает поиск сразу же после обнаружения файла с заданным именем. Если задать полную спецификацию файла, заключенную в двойные кавычки ( » » ), то препроцессор использует её для поиска и игнорирует стандартный каталог.
Если заключенная в двойные кавычки спецификация файла является неполной, то препроцессор сначала ищет каталог «родительского» файла. Родительский файл это файл, содержащий директиву #include. Например, если файл f2 вставляется в файл f1, то f1 будет родительским файлом.
Вставка файлов может быть вложенной. Т. е. директива #include может появляться в файле, который сам вставляется директивой #include. Файл f2 может вызывать файл f3. В этом случае f1 все еще будет родительским для f2, но «дедушкой» для f3.
При вложенной вставке файлов поиск каталогов начинается с родительского файла, затем проходит по дедушкиным файлам. Следовательно, поиск начинается в каталоге, который содержит обрабатываемый исходный файл. Если файл не найден, то поиск продолжается в каталогах, заданных в командной строке компилятора. И, наконец, производится поиск в стандартном каталоге.
Если спецификация файла заключена в угловые скобки ( ), то препроцессор не проводит поиска в текущем рабочем каталоге. Поиск файла начинается в каталогах, заданных в командной строке компилятора, а затем в стандартном каталоге.
Допускается вложение вставки файлов до 10 уровней. При обработке вложенных #include препроцессор всегда будет осуществлять вставку в первоначальный исходный файл.
Директива #undef [ править ]
Директива #undef удаляет текущее определение идентификатора. Поэтому все встречающиеся появления идентификатора будут игнорироваться предпроцессором. Для удаления определения макро с использованием #undef, нужно задать только идентификатор макро, не задавая список параметров.
Можно применить директиву #undef к идентификатору, у которого нет определения. Тем самым пользователь получает дополнительную гарантию того, что данный идентификатор не определён.
Директива #undef обычно используется в паре с директивой #define для задания области исходной программы, в которой идентификатор имеет специальное значение. Например, некоторая функция исходной программы может иметь объявленные константы, которые задают значения среды работы, которые не влияют на остальную часть программы. Директива #undef также работает с директивой #if для управления условной компиляцией исходной программы.
Условные директивы #if, #ifdef, #else, #endif, #elif [ править ]
Эти директивы позволяют подавить компиляцию части исходного файла, проверяя постоянное выражение или идентификатор. Результат проверки определяет, какие блоки текста будут переданы в компилятор и какие блоки текста будут удалены из исходного файла при предпроцессорной обработке.
Условная компиляция [ править ]
Директива #line [ править ]
Изменяет внутренний номер строки и имя файла компилятора. Если имя файла опущено, оно остается прежним. Cинтаксис директивы:
к примеру #line 1000 «file.сpp» устанавливается имя исходного файла file.сpp и текущий номер строки 1000. Текущий номер строки и имя файла доступны через константы препроцессора __LINE__ и __FILE__.
Директива #error [ править ]
Директива #error создаёт заданное пользователем сообщение об ошибке во время компиляции, а затем завершает компиляцию.
Примечание: Сообщение об ошибке, создаваемое этой директивой, содержит параметр token-string. Параметр token-string не подлежит расширению макроса. Эта директива наиболее полезна в ходе предварительной обработки и позволяет уведомлять разработчика о противоречиях в программе или о нарушении ограничений. В следующем примере демонстрируется обработка ошибки во время предварительной обработки.
Директива #pragma [ править ]
#pragma это инструкция компилятору, которая определяется реализацией. Конструкция #pragma в языке Си/Си++ используется для задания дополнительных указаний компилятору. С помощью этих конструкций можно указать как осуществлять выравнивание данных в структурах, запретить выдавать определённые предупреждения и так далее.
Директива #define Arduino IDE
Директива #define в Arduino IDE позволяет задавать имена значениям (константам), которые делают скетч более понятным. Т.е. можно в начале программы один раз определить имя константы или фрагмента кода, а затем использовать в скетче только это название. Рассмотрим на примерах с описанием правильные варианты использования функции #define в языке программирования Arduino IDE.
Arduino define описание директивы
Синтаксис директивы:
#define
При использовании директивы дефайн следует избегать использования имени другой переменной, константы или команды Ардуино, иначе оно оно будет заменено при компиляции. И обратите внимание, что в строчке не ставится точка с запятой и знак равенства, как это происходит при объявлении переменной, иначе компилятор выдаст ошибку. Рассмотрим использование #define на примере с подробным описанием.
Пример директивы: Arduino define pin
Это часть кода, от примера с мигающим трехцветным светодиодом. В данном примере мы присвоили имена для пинов 11, 12 и 13, к которым подключен светодиод. При написании кода нам удобнее использовать имена вместо номеров, чтобы каждый раз не вспоминать какой цвет к какому пину подключен. А программа автоматически будет заменять имена RED, GRN, BLU на соответствующие значения при компиляции.
Команды #ifdef, #ifndef и #endif в скетче
Инструкция #ifdef Arduino IDE проверят, было ли встречено в программе данное определение ранее, если было, то ставится блок кода с последующей строки и до #endif. В примере проверяется был ли ранее в #define определен признак отладки, если да, то код (вывод сообщения на монитор порта Arduino IDE) будет выполнен, если признак не определен, то сообщение на мониторе выводиться не будет.
Инструкции #ifndef проверят, было ли встречено в программе данное определение ранее и, если не было, то ставится блок кода с последующей строки и до #endif. В следующем простом примере мы объявляем новую константу, если только не объявляли ее в скетче ранее. Если дефайн с таким именем уже использовался, то программа проигнорирует строчки внутри конструкции #ifndef … #endif.
Замена функций с помощью define Arduino
Кроме использования дефайн в программе для объявления констант, можно заменять целые фрагменты кода с помощью директивы #define. Это более сложный, но интересный вариант использования define, который позволяет создать много разных упрощающих инструкций в скетче. Например, мы можем в первом примере заменить функцию pinMode() на конструкцию с дефайн с заданными параметрами.
В примере мы закодировали команду pinMode() одним словом «out». Теперь, где в скетче встретится слово «out», компилятор подставит строку pinMode(pin, OUTPUT) с заданным параметром pin. Таким же образом можно заменить команды digitalWrite() и delay(). Используя RGB светодиод или три обычных светодиода с Ардуино вы можете проверить работу следующего примера скетча с директивой дефайн.
Обратите внимание, что on(11, 500) и другие строчки не являются функциями, конструкция просто подставляет в код нужный текст. В более сложных программах есть риск создать самому ошибки, так как в скетче могут быть десятки подключаемых библиотек, где дефайн может что-то незаметно поменять. При этом будут возникать ошибки компиляции или ошибки во время исполнения программы.
Arduino define или const, что выбрать
Иногда бывает не удобно применять директиву #define для создания констант, в этом случае используют ключевое слово const. В отличие от глобальных переменных, значение const должно быть определено сразу при объявлении константы. Помните, что при использовании #define имена следует делать максимально уникальными, чтобы не было совпадений с командами из подключаемых библиотек.
Если использовать константу вместо дефайн в скетче из первого примера, то результат будет одинаковый – в коде вместо переменной RED будет подставляться цифра 11. На константы в программе действуют общие правила области видимости глобальных и локальных переменных. Кроме того, использованием #define или const не дает никаких преимуществ, с точки зрения экономии объема памяти микроконтроллера.
Директива #define
Директива #define
представляет собой набор лексем, таких как ключевые слова, константы, идентификаторы или выражения. Один или более пробельных символов должны отделять от (или от заключенных в скобки параметров). Если текст не умещается на строке, то он может быть продолжен на следующей строке; для этого следует набрать в конце строки символ обратный слэш и сразу за ним нажать клавишу ENTER.
может быть опущен. В этом случае все экземпляры будут удалены из исходного текста программы. Тем не менее, сам рассматривается как определенный и при проверке директивой #if дает значение 1 (смотри раздел 7.4.1).
, если он задан, содержит один или более идентификаторов, разделенных запятыми. Идентификаторы в списке должны отличаться друг от друга. Их область действия ограничена макроопределением, в котором они заданы. Список должен быть заключен в круглые скобки. Имена формальных параметров в отмечают позиции, в которые должны быть подставлены фактические аргументы макровызова. Каждое имя формального параметра может появиться в произвольное число раз.
Примечание. Не следует путать подстановку аргументов в макроопределение с передачей аргументов функции. Подстановка в препроцессоре носит чисто текстовый характер. Никаких вычислений или преобразований типа при этом не производится.
Выше уже говорилось, что макроопределение может содержать более одного вхождения данного формального параметра. Если формальный параметр представлен выражением с побочным эффектом, то это выражение будет вычисляться более одного раза, а вместе с ним каждый раз будет возникать и побочный эффект. Результат выполнения в этом случае может быть ошибочным.
После того как выполнена макроподстановка, полученная строка вновь просматривается для поиска других имен констант и макроопределений. При повторном просмотре не принимается к рассмотрению имя ранее произведенной макроподстановки. Поэтому директива
не приведет к зацикливанию препроцессора.
#define LENGTH (WIDTH + 10)
#define FILEMESSAGE «Попытка создать файл
не удалась из-за нехватки дискового пространства»
#define REG1 register
#define REG2 register
В первом примере идентификатор WIDTH определяется как целая константа со значением 80, а идентификатор LENGTH — как текст (WIDTH + 10). Каждое вхождение идентификатора LENGTH в исходный файл будет заменено на текст (WIDTH + 10), который после расширения идентификатора WIDTH превратится в выражение (80 + 10). Скобки, окружающие текст (WIDTH + 10), позволяют избежать ошибок в операторах, подобных следующему:
var = LENGTH * 20;
После обработки препроцессором оператор примет вид:
Значение, которое присваивается var, равно 1800. В отсутствие скобок в макроопределении оператор имел бы следующий вид:
Значение var равнялось бы 280, поскольку операция умножения имеет более высокий приоритет, чем операция сложения.
Во втором примере определяется идентификатор FILEMESSAGE. Его определение продолжается на вторую строку путем использования символа обратный слэш непосредственно перед нажатием клавиши ENTER.
В третьем примере определены три идентификатора, REG1, REG2, REG3. Идентификаторы REG1 и REG2 определены как ключевые слова register. Определение REG3 опущено и, таким образом, любое вхождение REG3 будет удалено из исходного файла. В разделе 7.4.1 приведен пример, показывающий, как эти директивы могут быть использованы для задания класса памяти register наиболее важным переменным программы.
В четвертом примере определяется макроопределение МАХ. Каждое вхождение идентификатора МАХ в исходном файле заменяется на выражение ((x)>(у))?(x):(у), в котором вместо формальных параметров х и у подставлены фактические. Например, макровызов
заменится на выражение
заменится на выражение
Обратите внимание на то, что в этом макроопределении аргументы с побочными эффектами могут привести к неверным результатам. Например, макровызов
заменится на выражение
Операнды операции > могут быть вычислены в любом порядке, а значение переменной i зависит от порядка вычисления. Поэтому результат выражения непредсказуем. Кроме того, возможна ситуация, когда переменная i будет инкрементирована дважды, что, вероятно, не требуется.
В пятом примере определяется макроопределение MULT. Макровызов MULT(3,5) в тексте программы заменяется на (3)*(5). Круглые скобки, в которые заключаются фактические аргументы, необходимы в тех случаях, когда аргументы макроопределения являются сложными выражениями. Например, макровызов
заменится на (3+4)*(5+6), что равняется 76. В отсутствие скобок результат подстановки 3+4*5+6 был бы равен 29.
Читайте также
Новая директива, начинающаяся с точки
4. Опережающие описания и подключение подпрограмм. Директива
4. Опережающие описания и подключение подпрограмм. Директива В программе может содержаться несколько подпрограмм, т. е. структура программы может быть усложнена. Однако эти подпрограммы могут располагаться на одном уровне вложенности, поэтому сначала должно идти
R.16.9 Пустая директива
R.16.9 Пустая директива Команда препроцессора вида#не оказывает никакого
Правило 2: Предпочитайте const, enum и inline использованию #define
Правило 2: Предпочитайте const, enum и inline использованию #define Это правило лучше было бы назвать «Компилятор предпочтительнее препроцессора», поскольку #define зачастую вообще не относят к языку C++. В этом и заключается проблема. Рассмотрим простой пример; попробуйте написать
4. Символьные строки директива #define, функции printf( ) и scanf( )
ДИРЕКТИВЫ ПРЕПРОЦЕССОРА #define, #include, #undef, #if, #ifdef, #ifndef, #else, #endif
ДИРЕКТИВЫ ПРЕПРОЦЕССОРА #define, #include, #undef, #if, #ifdef, #ifndef, #else, #endif Язык Си был разработан в помощь работающим программистам, а им нравится его препроцессор. Этот полезный помощник просматривает программу до компилятора (отсюда и термин «препроцессор») и заменяет
Директива #undef
Директива обработки ошибок
Директива обработки ошибок В СП ТС реализована директива #error. Ее формат:#error Обычно эту директиву записывают среди директив условной компиляции для обнаружения некоторой недопустимой ситуации. По директиве #error препроцессор прерывает компиляцию и выдает
Пустая директива
Пустая директива Для повышения читабельности программ СП ТС распознает пустую директиву, состоящую из строки, содержащей просто знак #. Эта директива всегда
3.12. Директива typedef
3.12. Директива typedef Директива typedef позволяет задать синоним для встроенного либо пользовательского типа данных. Например:typedef double wages;typedef vectorint vec_int;typedef vec_int test_scores;typedef bool in_attendance;typedef int *Pint;Имена, определенные с помощью директивы typedef, можно использовать точно так же, как
7.7. Директива связывания extern «C» A
7.7. Директива связывания extern «C» A Если программист хочет использовать функцию, написанную на другом языке, в частности на С, то компилятору нужно указать, что при вызове требуются несколько иные условия. Скажем, имя функции или порядок передачи аргументов различаются в
9.1.5. Директива extern «C» и перегруженные функции A
9.1.5. Директива extern «C» и перегруженные функции A В разделе 7.7 мы видели, что директиву связывания extern «C» можно использовать в программе на C++ для того, чтобы указать, что некоторый объект находится в части, написанной на языке C. Как эта директива влияет на объявления
Параллельные секции и директива parallel sections
Синхронизация и директива critical
Синхронизация и директива critical Директива critical исключает параллельное выполнение следующего за ней оператора. <$omp critical имя>оператор; Этот оператор образует критическую секцию – участок кода, который не может выполняться одновременно несколькими потоками.Только




