Гарантия неравенства указателей на стандартные функции?
Гарантирует ли язык C, что указатели на стандартные функции с разными именами должны сравниваться не одинаково?
Согласно 6.5.9 Операторы равенства, №6,
Два указателя сравниваются равными тогда и только тогда, когда оба являются нулевыми указателями, оба являются указателями на один и тот же объект (включая указатель на объект и подобъект в его начале) или функцию...
Кажется, я вспоминаю, что видел интерпретацию, утверждающую, что псевдонимы (множественные идентификаторы для "одной и той же функции") допустимы для стандартных функций, причем канонические кандидаты на такое обращение являются getc==fgetc
а также putc==fputc
; однако, я не знаю, где я мог это увидеть, и я скептически отношусь к этой концепции.
Есть ли официальное толкование или хорошо принятый аргумент за или против этой возможности?
2 ответа
Ну, вы попадаете в детали реализации. Стандарт только определяет поведение функций стандартной библиотеки.
За getc
спецификация говорит (подчеркните мою):
Функция getc эквивалентна fgetc, за исключением того, что если она реализована в виде макроса, она может оценивать поток более одного раза, поэтому аргумент никогда не должен быть выражением с побочными эффектами.
Таким образом, реализация может реализовать getc
как макрос, но он также может реализовать его как псевдоним (простой указатель на функцию) fgetc
или как другая функция с таким же поведением. Короче говоря, вы не можете положиться на &getc == &fgetc
быть правдой или ложью.
Единственное, что требует стандарт, это то, что '&getc` должно быть определено в 7.1.4 § 1:
... разрешено брать адрес библиотечной функции, даже если он также определен как макрос...
Это просто означает, что реализация должна иметь функцию с таким именем, но она может:
- быть
fgets
функция - хорошо&fgetc == &getc
правда - использовать макрос -
fgetc == &getc
ложно - позвонить
fgets
функция -&fgetc == &getc
ложно
Нет, я не верю, что есть такая гарантия.
Я полагаю, что вся эта дискуссия происходит от той части стандарта, которая позволяет функции также определяться как макрос с тем же именем.
От C17 7.1.4:
Любая функция, объявленная в заголовке, может быть дополнительно реализована как функционально-подобный макрос, определенный в заголовке, поэтому, если библиотечная функция объявлена явно, когда включен ее заголовок, один из методов, показанных ниже, может использоваться для обеспечения того, чтобы объявление не было пострадал от такого макроса. Любое макроопределение функции может быть подавлено локально, заключив имя функции в круглые скобки, потому что тогда за именем не следует левая скобка, которая указывает на расширение имени макрофункции. По той же синтаксической причине разрешается брать адрес библиотечной функции, даже если он также определен как макрос189).
189) Это означает, что реализация должна предоставлять фактическую функцию для каждой библиотечной функции, даже если она также предоставляет макрос для этой функции.
Текст продолжает описывать, как пользователи могут #undef
имя макроса, если они хотят гарантировать, что они получают фактическую функцию.
Таким образом, реализация может иметь стандартную функцию и макрос с одинаковым именем. Но то, что макрос затем расширяется, определяется реализацией. Это вполне может быть внутренняя функция с тем же адресом, что и у другого макроса библиотеки.
Исходя из этого, я не верю, что есть какие-либо гарантии того, что разные функции имеют разные адреса.
В конкретном случае getc
Стандарт гласит (C17 7.21.7.5):
Функция getc эквивалентна fgetc, за исключением того, что если она реализована как макрос, она может вычислять
stream
более одного раза, поэтому аргумент никогда не должен быть выражением с побочными эффектами.
Я бы сказал, что вполне вероятно, что реализация вызывает ту же самую фактическую функцию для fgetc
а также getc
когда они реализованы как макросы. (Или это atoi
против strtol
вызовите ту же функцию и т. д. и т. д.). Реализации стандартной библиотеки, на которые я заглянул, похоже, не делают этого, но я не думаю, что в стандарте есть что-то, что могло бы их остановить.
(Как примечание, взятие адреса библиотечных функций может быть не очень хорошей идеей по другим причинам, а именно, что оно может блокировать встраивание этой функции в один и тот же модуль перевода.)