Разве неправильно размещать встроенные функции в заголовках C?
Я строю проект на C для нескольких компиляторов, некоторые из которых являются устаревшими компиляторами, которые, похоже, не имеют поддержки со временем компоновки ссылок, поэтому было бы логично разместить static inline
функции непосредственно в заголовках и фактически каждый блок перевода имеет свою собственную копию.
Кроме того, мне нужно убедиться, что некоторые функции встроены, чтобы не вызывать другие функции (например, изменения в регистрах ЦП) при вызове внутри определенных низкоуровневых обработчиков прерываний, поэтому речь идет не просто о том, чтобы позволить компилятору выбирать, повлияет ли это на производительность.,
Однако, мой коллега сказал мне, что это необычное занятие, и я должен избегать этого. На данный момент в проекте я, вероятно, все еще могу переставить все, поэтому я просто хотел бы подтвердить, есть ли какие-то проблемы, с которыми мы могли бы столкнуться в долгосрочной перспективе, если мы решим использовать встроенные заголовки?
3 ответа
От n1570 (последний публичный проект C11), §6.7.4:
- Функция, объявленная с
inline
Спецификатор функции является встроенной функцией. Создание функции как встроенной функции предполагает, что вызовы функции должны быть максимально быстрыми. Степень эффективности таких предложений определяется реализацией.
В следующем разделе подробно рассказывается о связи, но этот отрывок выше - в основном все, что должен сказать о стандарте C inline
, Обратите внимание, что это дает реализации любую свободу, включая полное игнорирование inline
,
Поэтому, используя только стандартный C, вы можете получить несколько экземпляров (по одному на единицу перевода) функции, которая вызывается обычным способом. Обычно это не то, что вам нужно, так как сочетает в себе два недостатка (дублированный код и накладные расходы при вызове функции). Так что я бы поспорил стандартный C inline
полезен только для функций, приватных для одного модуля перевода. Даже в этом случае можно предположить, что хороший оптимизирующий компилятор будет автоматически выбирать кандидатов для встраивания без явного inline
,
Если, с другой стороны, ваш компилятор предоставляет способ принудительно встроить функцию (которая, согласно вашим комментариям, _inline
спецификатор для вашего компилятора), наличие этих функций в заголовке безопасно. Но имейте в виду, что это ни в коем случае не портативно.
Как прокомментировал cmaster, вы можете добиться своего рода " ручного встраивания " с помощью функциональных макросов вместо переносимого решения.
Согласно комментариям по вопросу, определяющим static _inline
Функции в заголовочных файлах - путь к этому конкретному компилятору, цитата из документа компилятора об этом ясна.
Проблема, с которой вы можете столкнуться позже, заключается в том, что это конкретное расширение для определенного компилятора, отличное от стандартного. inline
спецификатор. Как сказал Феликс, стандарт позволяет реализации выбирать, как он реализует встраивание, и, в частности, игнорирование спецификатора все равно будет соответствовать:
Создание функции как встроенной функции предполагает, что вызовы функции должны быть максимально быстрыми. Степень эффективности таких предложений определяется реализацией.
При этом семантика кажется одинаковой (из проекта 1256 для C99 или проекта 1570 для C11):
6.7.4 Спецификаторы функций
...
Любая функция с внутренней связью может быть встроенной функцией. Для функции с внешним связыванием применяются следующие ограничения: если функция объявлена со встроенным спецификатором функции, то она также должна быть определена в той же единице перевода. Если все объявления области действия файла для функции в модуле перевода включают в себя спецификатор встроенной функции без extern, то определение в этом модуле перевода является встроенным определением. Встроенное определение не предоставляет внешнего определения для функции и не запрещает внешнее определение в другой единице перевода. Встроенное определение предоставляет альтернативу внешнему определению, которое переводчик может использовать для реализации любого вызова функции в том же модуле перевода. Не определено, использует ли вызов функции встроенное определение или внешнее определение
Так как общие реализации делают честь inline
спецификатор, мое мнение таково, что риск переносимости ограничен
Нет необходимости объявлять функцию как static inline
как ISO 9899
гарантирует, что функция не будет экспортирована компоновщику как раз, поскольку вы не используете спецификатор класса хранения extern
в любом объявлении встроенной функции в единице перевода.
Другими словами, если вы просто объявите это как inline aaa
, символ aaa
не будет виден снаружи.
Я цитирую 6.7.4 Function specifiers
Если все объявления области видимости файла для функции в модуле перевода включают спецификатор встроенной функции без extern, тогда определение в этом модуле перевода является встроенным определением. Встроенное определение не предоставляет внешнего определения для функции и не запрещает внешнее определение в другой единице перевода.
Emacs использует эту технику встраивания в lisp.h
заголовок, который определяет объекты lisp, я цитирую из исходного кода emacs:
Некоторые операции выполняются настолько часто, что они реализуются в виде макросов, а не функций, потому что в противном случае производительность во время выполнения сильно пострадает при компиляции с GCC без оптимизации.
Нет необходимости встраивать все, кроме операций, которые в противном случае могли бы вызвать серьезную проблему с производительностью.