Стандарт C требует, чтобы определяемые им функции были определены как функции, и они должны быть объявлены в соответствующих заголовках, чтобы можно было передавать указатели на функции вокруг.
C Стандарт позволяет переопределять функции с помощью функционально-подобных макросов.
Стандарт содержит блок правил solid, который я переформатировал в виде списка маркеров. Первые два пункта не имеют прямого отношения к вопросу.
Each1 Каждый из Следующие утверждения применимы, если явно не указано иное в подробных описаниях, которые приведены ниже:
- Если аргумент функции имеет недопустимое значение (например, значение вне домена функции или указатель вне адресное пространство программы, или нулевой указатель, или указатель на немодифицируемое хранилище, когда соответствующий параметр не квалифицирован как const), или тип (после продвижения), не ожидаемый функцией с переменным числом аргументов, поведение undefined.
- Если аргумент функции описан как массив, указатель, фактически переданный в функцию, должен иметь значение, такое, что все вычисления адреса и доступ к объектам (это было бы допустимо, если бы указатель действительно указывал на первый элемент такого массива) действительно действителен.
- Любая функция Объект on, объявленный в заголовке, может быть дополнительно реализован как функциональный макрос, определенный в заголовке, поэтому, если библиотечная функция объявляется явно, когда включен ее заголовок, можно использовать один из методов, показанных ниже, чтобы гарантировать, что объявление не затронуто таким макросом.
- Любое макроопределение функции может быть локально подавлено путем заключения имени функции в круглых скобках, потому что за именем не следует левая скобка, которая указывает на расширение имени макро-функции. , По той же причине syntacti c разрешается брать адрес библиотечной функции, даже если он также определен как макрос. 185)
- Использование #undef для удаление любого определения макроса также обеспечит ссылку на фактическую функцию.
- Любой вызов библиотечной функции, которая реализована как макрос, должен расширяться до кода, который оценивает каждый из своих аргументов ровно один раз, полностью защищенный круглыми скобками. где это необходимо, поэтому обычно безопасно использовать произвольные выражения в качестве аргументов. 186)
- Аналогично, подобные функциональные макросы, описанные в следующих подпунктах, могут вызываться в выражении в любом месте функции с совместимым типом возвращаемого значения. 187)
- Все объектоподобные макросы, перечисленные как расширяющиеся до целочисленных константных выражений, должны дополнительно подходить для использования в
#if
директивах предварительной обработки.
¶2 При условии, что библиотечная функция может быть объявлена без ссылки Кроме того, для любого типа, определенного в заголовке, также допустимо объявлять функцию и использовать ее, не включая связанный с ней заголовок.
185) Это означает, что реализация должна предоставлять фактическую функцию для каждой библиотечной функции, даже если она также предоставляет макрос для этой функции.
186) Такие макросы могут не содержать точек последовательности, которые соответствующие вызовы функций делают.
187) Поскольку внешние идентификаторы и некоторые имена макросов, начинающиеся с подчеркивания, зарезервированы, реализации могут предоставлять специальную семантику для таких имен. Например, идентификатор _BUILTIN_abs
может использоваться для указания генерации встроенного кода для функции abs
. Таким образом, соответствующий заголовок может указывать
#define abs(x) _BUILTIN_abs(x)
для компилятора, чей генератор кода примет его. Таким образом, пользователь, желающий гарантировать, что данная библиотечная функция, такая как abs, будет подлинной функцией, может написать
#undef abs
обеспечивает ли заголовок реализации реализацию макроса abs
или встроенную реализацию. Тем самым раскрывается и прототип функции, который предшествует и скрыт любым определением макроса.
Заголовок в вопросе иллюстрирует использование зарезервированных идентификаторов (§7.1.3 Reserved идентификаторы] (http://port70.net/~nsz/c/c11/n1570.html#7 .1.3 )). Он объявляет функции, которые указаны для объявления заголовка <ctype.h>
. Он предоставляет макросы, которые переопределяют эти функции, полагая, что их использование будет быстрее, чем вызов функции, реализующей доступ к массиву.
Если реализация выполнена таким образом, если вам нужно передать указатель на один из Классификация или преобразование функций в другой код, вы можете сделать это. Если бы были предоставлены только макросы, вам нужно было бы выполнить некоторые трюки, чтобы фактические функции передавались как указатели.
Стандарт тщательно предусматривает, что несколько макросов действительно должны быть макросами - offsetof()
и va_start()
и va_arg()
- три, которые приходят на ум. Но подавляющее большинство функций в стандарте должны быть реализованы как функции - но могут быть переопределены макросом, если разработчики считают, что это уместно.
Требование, чтобы макросы были функциональными макросами, также важно. , Это позволяет использовать имя без скобок, чтобы получить указатель на функцию. Если бы макросы не были похожи на функции (если заголовок содержал что-то вроде #define isupper _IsUpper
вместо #define isupper(c) _IsUpper(c)
), тогда было бы невозможно рассчитывать на доступ к стандартному имени функции - тогда как правило ¶2 позволяет вам писать в своем коде (без включения <ctype.h>
):
extern int isupper(int c);
, и вам будет гарантировано, что в библиотеке есть функция isupper()
, которая соответствует ожиданиям (даже если есть также функция _IsUpper()
).