Когда я пытаюсь построить этот код
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
как в приведенном выше примере, компилируется согласованно только в том случае, если существует также внешняя функция с тем же именем, и я никогда не знаю, вызывается ли моя собственная функция или внешняя функция.
Разве такое поведение не совершенно глупо? Полезно ли когда-нибудь определять функцию inline
без C99 static
или extern
в нем? Я что-то упускаю?
Резюме ответов
Конечно, мне чего-то не хватало, и поведение не дурацкое. :)
Как объясняет Немо , идея состоит в том, чтобы поместить определение функции
inline void f() {}
в заголовочном файле и только объявление
extern inline void f();
в соответствующем файле .c. Только extern
объявление запускает генерацию двоичного кода, видимого извне. И действительно, inline
в файле .c нет использования - он полезен только в заголовках.
Как поясняет смысл комитета C99, цитируемый в ответе Джонатана , inline
все дело в оптимизации компилятора, которая требует, чтобы определение функции было видимым на месте вызова. Это может быть достигнуто только путем помещения определения в заголовок, и, конечно же, определение в заголовке не должно генерировать код каждый раз, когда его видит компилятор. Но поскольку компилятор не обязан фактически встраивать функцию, где-то должно существовать внешнее определение.
inline
не менее, существуют ли когда-либо варианты использования безstatic
иextern
. К сожалению, ни один из этих вопросов не затрагивается в этом вопросе.Ответы:
На самом деле этот отличный ответ также отвечает на ваш вопрос, я думаю:
Что делает extern inline?
Идея состоит в том, что «inline» можно использовать в файле заголовка, а затем «extern inline» в файле .c. «extern inline» - это то, как вы указываете компилятору, какой объектный файл должен содержать (видимый извне) сгенерированный код.
[обновить, уточнить]
Я не думаю, что в файле .c есть какой-то смысл для "inline" (без "static" или "extern"). Но в файле заголовка это имеет смысл, и для фактической генерации автономного кода требуется соответствующее объявление "extern inline" в каком-то файле .c.
источник
inline void f() {}
в шапке иextern inline void f();
в файле .c? Таким образом, фактическое определение функции находится в заголовке, а файл .c содержит простое объявление в этом случае в обратном порядке?inline
безstatic
илиextern
. Конечно,static inline
это нормально, но этот вопрос и ответ не об этом.-std=c99
вместо-std=gnu89
.Из самого стандарта (ISO / IEC 9899: 1999):
Комитет C99 написал Обоснование , в котором говорится:
источник
> Я получаю ошибку компоновщика (неопределенная ссылка наf
)Работает здесь: Linux x86-64, GCC 4.1.2. Может быть ошибка в вашем компиляторе; В процитированном абзаце стандарта я не вижу ничего, что запрещало бы данную программу. Обратите внимание на использование if, а не iff .Итак, если вы знаете поведение функции
f
и хотите вызвать ее в жестком цикле, вы можете скопировать и вставить ее определение в модуль, чтобы предотвратить вызовы функций; или вы можете предоставить определение, которое для целей текущего модуля эквивалентно (но пропускает проверку ввода или любую другую оптимизацию, которую вы можете себе представить). Однако у автора компилятора есть возможность оптимизации под размер программы.источник