Когда я пытаюсь создать этот код
inline void f() {}
int main()
{
f();
}
с помощью командной строки
gcc -std=c99 -o a a.c
Я получаю ошибку компоновщика (undefined ссылка на f
). Ошибка исчезает, если я использую static inline
или extern inline
вместо просто inline
, или если я скомпилирую с -O
(поэтому функция фактически встроена).
Такое поведение, как представляется, определено в пункте 6.7.4 (6) стандарта C99:
Если все объявления области видимости для функции в блоке трансляции включают спецификатор функции
inline
безextern
, тогда определение в этом блоке перевода является встроенным определением. Встроенное определение не предоставляет внешнего определения функции и не запрещает внешнее определение в другой единицы перевода. Встроенное определение предоставляет альтернативу внешнему определению, которое переводчик может использовать для реализации любого вызова функции в той же самой единице перевода. Не указано, использует ли вызов функции встроенное определение или внешнее определение.
Если я все правильно понимаю, блок компиляции с определенной функцией inline
, как в приведенном выше примере, только последовательно компилируется, если есть также внешняя функция с тем же именем, и я никогда не знаю, принадлежит ли моя собственная функция или вызывается внешняя функция.
Разве это не совсем глупо? Полезно ли когда-либо определять функцию inline
без static
или extern
в C99? Я что-то пропустил?
Сводка ответов
Конечно, я что-то упустил, и поведение не глупо.:)
Как Nemo объясняет, идея состоит в том, чтобы поместить определение функции
inline void f() {}
в файле заголовка и только объявление
extern inline void f();
в соответствующем .c файле. Только объявление extern
запускает генерацию внешнего видимого двоичного кода. И действительно, нет использования inline
в .c файле - это полезно только в заголовках.
Как поясняет обоснование комитета C99, приведенное в ответе Джонатана, inline
- это все, что связано с оптимизацией компилятора, требующей определения функции для видимости на сайт вызова. Это может быть достигнуто только путем помещения определения в заголовок, и, конечно, определение в заголовке не должно генерировать код каждый раз, когда это видно компилятору. Но поскольку компилятор не вынужден фактически встроить функцию, должно существовать внешнее определение.