Почему библиотека C использует макросы и функции с одинаковыми именами?

20

Я читаю «Стандартную библиотеку С» П. Дж. Плаугера, которая действительно интересна. Книга объясняет не только как использовать библиотеку, но и как она реализована.

Я закончил чтение ctype.hраздела и в шапке функции объявлены как макросы и функции. Например

int isdigit(int);

но и

#define isdigit(c) (_Ctype[(int)(c)] & _DI)

Я не понимаю, почему ОБА используются?

Кроме того, если я попытаюсь воссоздать свой собственный пользовательский ctypeзаголовок и реализацию, я могу успешно скомпилировать только после удаления макроса (закомментируйте определение).

Этот аспект не был действительно объяснен в книге. Может кто-нибудь объяснить, пожалуйста?

user619818
источник
В стандарте C нет ничего, что заставляло бы компилятор реализовывать его как макрос. Макрос, скорее всего, является остатком прежних дней, когда у С не было встроенных символов. Хотя умный компилятор должен иметь возможность встроить эту функцию, если необходимо, без явного встроенного ключевого слова. Таким образом, подобный функции макрос существует только потому, что компилятор был реализован кем-то, кто не был великолепен в создании компиляторов.

Ответы:

23

Макрос (предположительно) более эффективен, поскольку не требует вызова функции. Его можно оптимизировать проще, поскольку он включает в себя поиск смещения указателя.

Вызов функции позволяет создавать ссылки на одну и ту же библиотеку, даже если программа была скомпилирована без определения макроса - если она была скомпилирована с другим заголовком или просто с мошенническим объявлением внутри исходного файла. Если, например, у вас есть компилятор, у которого есть чья-то «улучшенная» версия ctype.h, у которой нет макроса, функция все равно будет существовать во время выполнения для использования.

Если мы посмотрим на стандарт:

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

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

Это означает, что если вы напишите:

int b = (isdigit)(c);

или

int (*f)(int) = &isdigit;
int b = f(c);

тогда вы вызываете реальную функцию, а не макрос. Вы также можете по закону написать:

#undef isdigit
int b = isdigit(c);

или (в исходном файле нет #include <ctype.h>прямого или транзитивного):

extern int isdigit(int);
int b = isdigit(c);
ecatmur
источник
Как программа может быть скомпилирована без определения макроса?
user619818
@ user619818 используя extern int isdigit(int), например.
Ecatmur
Просто для ясности, вы имеете в виду, что у пользователя нет #include <what>, а вместо этого добавляется int isdigit (int); в верхней части файла реализации. Хорошо, просто внимательно прочитайте свой ответ.
user619818
Наличие вызываемой функции (вместо встроенного макрокода) также позволяет другим языкам, таким как python и perl, взаимодействовать с этими функциями
technosaurus