Использование переопределений макросов для функций - PullRequest
1 голос
/ 05 апреля 2020

Я читал реализацию заголовка в библиотеке C, где я встречал переопределения макросов для функций, а также объявления функций. Я хочу знать, как это полезно, .ie или макрос должен использоваться или функции, что нужно для переопределения?

РЕДАКТИРОВАТЬ: Пример:

/* ctype.h standard header */
#ifndef _CTYPE
#define _CTYPE
/* Ctype code b i t s */
#define 0x200 /* extra alphabetic */
#define _XS 0x100 /* extra space */
#define _BB 0x80 /* BEL, BS, etc. */
#define _CN 0x40 /*CR, FF, HT, NL, V T */
#define _DI 0x20 /* '0'_' 9' */
#define _LO 0x10 /* 'a'_'2'*/
#define _PU 0x08 /* punctuation */
#define _SP 0x04 /* space */
#define _UP 0x02 /* 'A' _ ' Z ' */
#define _XD 0x01 /* 'Or_'9', 'A'_'Fr, ' a r _ ' f r * /
/* ********declarations********** */
int isalnum(int) , isalpha (int) , iscntrl (iny) , isdigit (int) ;
int isgraph (int) , islower (int) , isprint (int) , ispunct (int) ;
int isspace (int) , isupper (int) , isxdigit (int) ;
int tolower (int) , toupper (int) ;
extern const short *_Ctype, *_Tolower, *_Toupper;

/************ macro overrides*********** */
#define isalnum(c) (_Ctype [ (int)(C) ] & (_DI | _LO | _UP | _XA) )
#define isalpha (c) (_Ctype [ (int)(C) ] & (_LO | _UP | _XA) )
#define i s c n t r l (c) (_Ctype [ (int)(C) ] & (_BB | _CN) )
#define isdigit (c) (_Ctype [ (int)(C) ] & _DI)
#define isgraph (c) (_Ctype [ (int)(C) ] & (_DI | _LO| _PU| _UP | _XA) )
#define islower (c) (_Ctype [ (int)(C) ] & _LO)
#define isprint (c) \
(_Ctype[(int) (c)1 & (_DI| _LO| _PU| _SP| _UP| _XA))
#define ispunct (c) (_Ctype [ ( int ) (c) ] & _PU)
#define isspace (c) (_Ctype [ ( int ) (c) ] & (_CN | _SP | _XS) )
#define isupper (c) (_Ctype [ ( int ) (c) ] & _UP)
#define isxdigit (c) (_Ctype [ ( int ) (c) ] & _XD)
#define tolower (c) _Tolower [ ( int ) (c) ]
#define toupper (c) _Toupper [ ( int ) (c) ]
#endif

* Не говоря уже о функции определения находятся в отдельных файлах

Ответы [ 3 ]

4 голосов
/ 05 апреля 2020

Стандарт C требует, чтобы определяемые им функции были определены как функции, и они должны быть объявлены в соответствующих заголовках, чтобы можно было передавать указатели на функции вокруг.

C Стандарт позволяет переопределять функции с помощью функционально-подобных макросов.

Стандарт содержит блок правил solid, который я переформатировал в виде списка маркеров. Первые два пункта не имеют прямого отношения к вопросу.


§7.1.4 Использование библиотечных функций

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()).

0 голосов
/ 05 апреля 2020

Фактический интерфейс состоит из трех массивов, например, массив _Ctype возвращает тип символа. Макросы, например макросы is ... предназначены для удобства использования массива _Ctype.

Я бы сказал, что массивы фиксированы, макросы могут быть адаптированы к вашему варианту использования. Вы можете написать новые макросы, изменить существующие. Или вообще не включайте макросы.

Подход здесь для обеспечения скорости и совместимости. С современными конструкциями C ++ такой интерфейс можно было бы написать по-другому.

0 голосов
/ 05 апреля 2020

Нет никакой причины использовать макрос вместо функции. В старые времена, возможно, было повышение эффективности, но современные компиляторы вполне способны включать вызовы функций там, где это необходимо.

Вы должны будете привести пример того, что вы подразумеваете под Переопределение макроса. Объявление функции и объявление макроса с одинаковыми именами не могут разумно сосуществовать. Например, трудно вызвать функцию после определения макроса, вам придется сначала отменить определение макроса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...