Определение функций C в стандартной библиотеке C как макросов
Я читал Открытые базовые спецификации групп и справочные страницы программистов Posix по заголовкам C, и эта фраза многократно повторяется: "Следующее должно быть объявлено как функции, а также может быть определено как макрос", и я не совсем уверен, что это значит. Какой смысл создавать макрос из объявленной функции. Разве это не создаст конфликт пространства имен? Кроме того, почему вы хотите это сделать?
2 ответа
Макросы никогда не конфликтуют с другими пространствами имен; макросы обрабатываются первыми и автоматически "выигрывают".
Раздел "Введение" раздела "Стандартная библиотека C" стандарта, раздел 7.1 "Введение" - и, в частности, §7.1.2, §7.1.3 и .47.1.4 - охватывает многие из пунктов.
Есть некоторые функции, которые могут быть реализованы более эффективно с помощью макроса без дополнительных затрат на полный вызов функции. Правила стандарта позволяют реализации определять макросы для таких функций с учетом ряда предостережений. Во-первых, аргументы макроса не могут использоваться более одного раза; другое заключается в том, что должна быть функция для каждой функции, определенной в стандарте, даже если она также реализована в виде макроса.
Правила допускают гибкость в реализации без чрезмерной нагрузки на разработчиков или пользователей реализации.
§7.1.2 Стандартные заголовки
…4 … Если используется, заголовок должен быть включен за пределы любого внешнего объявления или определения, и он должен сначала быть включен перед первой ссылкой на любую из функций или объектов, которые он объявляет, или на любой из типов или макросов, которые он определяет. Однако, если идентификатор объявлен или определен в более чем одном заголовке, второй и последующие связанные заголовки могут быть включены после начальной ссылки на идентификатор. В программе не должно быть никаких макросов с именами, лексически идентичными ключевым словам, определенным в настоящее время до включения заголовка или когда любой макрос, определенный в заголовке, раскрывается.
Any5 Любое определение объектоподобного макроса, описанное в этом пункте, должно расширяться до кода, который полностью защищен круглыми скобками, где это необходимо, чтобы он группировался в произвольном выражении, как если бы это был один идентификатор.
Any6 Любое объявление библиотечной функции должно иметь внешнюю связь.
.17.1.3 Зарезервированные идентификаторы
1 Каждый заголовок объявляет или определяет все идентификаторы, перечисленные в связанном с ним подпункте, и необязательно объявляет или определяет идентификаторы, перечисленные в соответствующем подпункте будущих направлений библиотеки, и идентификаторы, которые всегда зарезервированы для любого использования или для использования в качестве идентификаторов области файла.
Все идентификаторы, которые начинаются со знака подчеркивания, а также заглавной буквы или другого знака подчеркивания, всегда зарезервированы для любого использования.
Все идентификаторы, которые начинаются с подчеркивания, всегда зарезервированы для использования в качестве идентификаторов с областью действия файла как в обычном пространстве, так и в пространстве имен тега.
Каждое имя макроса в любом из следующих подпунктов (включая будущие направления библиотеки) зарезервировано для использования, как указано, если включен какой-либо из связанных с ним заголовков; если прямо не указано иное (см. 7.1.4).
Все идентификаторы с внешней связью в любом из следующих подпунктов (включая будущие направления библиотеки) и errno всегда зарезервированы для использования в качестве идентификаторов с внешней связью. 184)
Каждый идентификатор с областью файла, указанной в любом из следующих подпунктов (включая будущие направления библиотеки), зарезервирован для использования в качестве имени макроса и в качестве идентификатора с областью файла в том же пространстве имен, если включен какой-либо из связанных с ним заголовков.
¶2 Другие идентификаторы не зарезервированы. Если программа объявляет или определяет идентификатор в контексте, в котором она зарезервирована (за исключением случаев, разрешенных в 7.1.4), или определяет зарезервированный идентификатор как имя макроса, поведение не определено.
If3 Если программа удаляет (с
#undef
) любое макроопределение идентификатора в первой группе, указанной выше, поведение не определено.184) Список зарезервированных идентификаторов с внешней связью включает
math_errhandling
,setjmp
,va_copy
, а такжеva_end
,.17.1.4 Использование библиотечных функций
Each1 Каждое из следующих утверждений применимо, если явно не указано иное в подробных описаниях, которые следуют: Если аргумент функции имеет недопустимое значение (например, значение вне домена функции или указатель вне адресного пространства программа, или нулевой указатель, или указатель на немодифицируемое хранилище, когда соответствующий параметр не квалифицирован как const), или тип (после продвижения), не ожидаемый функцией с переменным числом аргументов, поведение не определено. Если аргумент функции описывается как массив, указатель, фактически переданный функции, должен иметь значение, такое, что все вычисления адреса и доступ к объектам (это было бы допустимо, если бы указатель действительно указывал на первый элемент такого массива) на самом деле действительны. Любая функция, объявленная в заголовке, может быть дополнительно реализована как функционально-подобный макрос, определенный в заголовке, поэтому, если библиотечная функция объявлена явно, когда включен ее заголовок, один из методов, показанных ниже, может использоваться для обеспечения того, чтобы объявление не было пострадал от такого макроса. Любое макроопределение функции может быть подавлено локально, заключив имя функции в круглые скобки, потому что тогда за именем не следует левая скобка, которая указывает на расширение имени макрофункции. По той же синтаксической причине разрешается брать адрес библиотечной функции, даже если он также определен как макрос. 185) Использование
#undef
удаление любого определения макроса также обеспечит ссылку на фактическую функцию. Любой вызов библиотечной функции, которая реализована в виде макроса, должен расширяться до кода, который оценивает каждый из своих аргументов ровно один раз, при необходимости он полностью защищен круглыми скобками, поэтому обычно безопасно использовать произвольные выражения в качестве аргументов. 186) Аналогично, подобные функциональные макросы, описанные в следующих подпунктах, могут вызываться в выражении везде, где может быть вызвана функция с совместимым типом возврата. 187) Все объектоподобные макросы, перечисленные как расширяющиеся до целочисленных константных выражений, должны дополнительно подходить для использования в директивах предварительной обработки #if.Prov2 При условии, что библиотечная функция может быть объявлена без ссылки на какой-либо тип, определенный в заголовке, также допустимо объявить функцию и использовать ее, не включая связанный с ней заголовок.
185) Это означает, что реализация должна предоставлять фактическую функцию для каждой библиотечной функции, даже если она также предоставляет макрос для этой функции.
186) Такие макросы могут не содержать точек последовательности, которые имеют соответствующие вызовы функций.
187) Поскольку внешние идентификаторы и некоторые имена макросов, начинающиеся с подчеркивания, зарезервированы, реализации могут предоставлять специальную семантику для таких имен. Например, идентификатор
_BUILTIN_abs
может использоваться для указания генерации встроенного кода дляabs
функция. Таким образом, соответствующий заголовок может указывать:#define abs(x) _BUILTIN_abs(x)
для компилятора, чей генератор кода примет его. Таким образом, пользователь желает гарантировать, что данная функция библиотеки, такая как
abs
будет подлинная функция может написать#undef abs
обеспечивает ли заголовок реализации реализацию макроса
abs
или встроенная реализация. Тем самым раскрывается также прототип функции, который предшествует и скрыт любым макроопределением.
Акцент добавлен в ¶7.1.4
В этих правилах изложены основные положения договора между разработчиком (компилятором и, в особенности, библиотекой C) и программистом, использующим эту реализацию.
Функции в стандартной библиотеке должны существовать как функции, потому что вы можете использовать их, даже если вы этого не сделаете #include
любой стандартный заголовок. То есть, компоновщик должен быть в состоянии найти функцию, и вы можете самостоятельно вставить объявление, если оно является правильным, и вы не включаете заголовок, который объявляет функцию.
Однако некоторые функции могут быть более эффективно реализованы как макросы. (putc
а также isdigit
два общих примера.) Если это так, то реализация может воспользоваться этим, также включив эффективное определение макроса.
Имена и функции макросов не находятся в одном и том же пространстве имен в C, поэтому никакого конфликта не будет. Макрос будет расширен там, где используется функция, что означает, что внешняя функция обычно не будет использоваться, но есть два важных способа, которыми символ, определенный как "подобный функции" макрос, не будет расширяться макросом:
Если символ используется в определении макроса, потому что C не допускает рекурсивное расширение макроса. Это полезно для функций, для которых общим случаем вызова может быть макрос, использующий библиотечную функцию как запасной вариант. (Например,
ctype
Функции могут быть определены как макросы, которые вызывают функцию, если текущая локаль не является локалью C.Если имя макроса используется без списка аргументов в скобках. Это позволяет вам взять адрес функции, даже если она имеет реализацию макроса. Вы (или стандартная библиотека) можете принудительно использовать функцию вместо макроса, используя избыточные скобки:
(isdigit)(my_char)