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