Каково использование ключевого слова `inline` в C?

Я прочитал несколько вопросов в stackoverflow о inline в C, но до сих пор неясно об этом.

  • static inline void f(void) {} не имеет практической разницы с static void f(void) {}.
  • inline void f(void) {} в C не работает как путь С++. Как это работает в C?
  • Что на самом деле делает extern inline void f(void);?

Я никогда не нашел использование ключевого слова inline в моих программах на C, и когда я вижу это ключевое слово в коде других людей, он почти всегда static inline, в котором я не вижу разницы только с static.

Ответы

Ответ 1

Примечание: когда я говорю о файлах .c и .h в этом ответе, я предполагаю, что вы правильно выложили свой код, т.е. файлы .c включают только файлы .h. Различие состоит в том, что файл .h может быть включен в несколько единиц перевода.

static inline void f(void) {} не имеет практического различия со static void f(void) {}.

В ISO C это правильно. Они идентичны по поведению (конечно, если вы не объявляете их по-разному в одном и том же TU!), Единственный практический эффект может заключаться в том, чтобы компилятор по-разному оптимизировал.

inline void f(void) {} в C не работает как C++. Как это работает в C? Что на самом деле делает extern inline void f(void); делать?

Это объясняется этим ответом, а также этой веткой.

В ISO C и C++ вы можете свободно использовать inline void f(void) {} в заголовочных файлах - хотя по разным причинам!

В ISO C он вообще не дает внешнего определения. В ISO C++ он дает внешнее определение; однако у C++ есть дополнительное правило (которого нет у C): если существует несколько внешних определений inline функции, компилятор разбирает ее и выбирает одно из них.

extern inline void f(void); в файле .c в ISO C подразумевается в сочетании с использованием inline void f(void) {} в заголовочных файлах. Это приводит к тому, что внешнее определение функции выдается в этой единице перевода. Если вы этого не сделаете, тогда не будет никакого внешнего определения, и, таким образом, вы можете получить ошибку ссылки (неизвестно, будет ли какой-либо конкретный вызов f ссылаться на внешнее определение или нет).

Другими словами, в ISO C вы можете вручную выбрать, куда идет внешнее определение; или полностью исключить внешнее определение, используя static inline везде; но в ISO C++ компилятор выбирает, куда и куда направить внешнее определение.

В GNU C все по-другому (подробнее об этом ниже).

Чтобы еще больше усложнить ситуацию, GNU C++ позволяет вам писать static inline строку intern extern inline in code C++... Я не хотел бы догадываться, что именно это делает

Я никогда не находил использование ключевого слова inline в моих программах на C, и когда я вижу это ключевое слово в коде других людей, оно почти всегда статично inline

Многие программисты не знают, что делают, и просто собирают что-то, что, кажется, работает. Еще одним фактором здесь является то, что код, который вы просматриваете, мог быть написан для GNU C, а не ISO C.

В GNU C обычный inline ведет себя иначе, чем ISO C. Он фактически испускает внешне видимое определение, поэтому наличие файла .h с простой inline функцией, включенной из двух блоков перевода, вызывает неопределенное поведение.

Так что, если кодировщик хочет поставить inline подсказку оптимизации в GNU C, то static inline требуются. Поскольку static inline работают как в ISO C, так и в GNU C, естественно, что люди в конечном итоге согласились с этим и увидели, что он работает без ошибок.

в котором я не вижу разницы только со статикой.

Разница только в том, что мы хотим предоставить компилятору подсказку по оптимизации скорости. С современными компиляторами это лишнее.

Ответ 2

Код AC можно оптимизировать двумя способами: для размера кода и для времени выполнения.

встроенные функции:

gcc.gnu.org говорит,

Объявляя встроенную функцию, вы можете указать GCC ускорить вызов этой функции. Один из способов, которым GCC может достичь этого, - интегрировать этот код функции в код для своих вызывающих. Это ускоряет выполнение за счет устранения накладных расходов на вызовы функций; кроме того, если любое из фактических значений аргумента является постоянным, их известные значения могут допускать упрощения во время компиляции, так что не весь код встроенной функции должен быть включен. Влияние на размер кода менее предсказуемо; объектный код может быть больше или меньше с встраиванием функции, в зависимости от конкретного случая.

Таким образом, он указывает компилятору встроить функцию в код, где она используется, с целью улучшения времени выполнения.

Если вы объявляете небольшие функции, такие как установка/очистка флага или некоторое переключение битов, которые выполняются многократно, inline, это может существенно повлиять на производительность по времени, но за счет размера кода.


нестатический inline и статический inline

Снова ссылаясь на gcc.gnu.org,

Если встроенная функция не является статической, то компилятор должен предполагать, что могут быть вызовы из других исходных файлов; поскольку глобальный символ может быть определен только один раз в любой программе, функция не должна быть определена в других исходных файлах, поэтому вызовы в них не могут быть интегрированы. Поэтому нестатическая встроенная функция всегда компилируется сама по себе обычным способом.


внешний встроенный?

И снова gcc.gnu.org говорит сам за себя:

Если вы укажете как inline, так и extern в определении функции, то это определение используется только для встраивания. Ни в коем случае функция не компилируется сама по себе, даже если вы явно ссылаетесь на ее адрес. Такой адрес становится внешней ссылкой, как если бы вы только объявили функцию и не определили ее.

Эта комбинация inline и extern имеет почти эффект макроса. Способ его использования - поместить определение функции в заголовочный файл с этими ключевыми словами и поместить еще одну копию определения (без встроенного и внешнего) в библиотечный файл. Определение в заголовочном файле приводит к тому, что большинство вызовов функции будут встроенными. Если какое-либо использование функции остается, они ссылаются на единственную копию в библиотеке.


Подвести итог:

  1. Для inline void f(void){} inline определение действительно только в текущей единице перевода.
  2. Для static inline void f(void) {} Поскольку класс хранения является static, идентификатор имеет внутреннюю связь, а inline определение невидимо в других единицах перевода.
  3. Для extern inline void f(void); Так как класс хранения extern, идентификатор имеет внешнее связывание и определение инлайн также обеспечивает внешнее определение.

Ответ 3

От 6.7.4 Спецификаторы функций в спецификациях C11

6 Функция, объявленная с помощью встроенного спецификатора функции, является встроенной функция. Выполнение функции встроенной функции предполагает, что вызовы функция должна быть как можно быстрее. 138) Степень, в которой такие предложения эффективны определяется реализациейсильной > . 139)

138) Используя, например, альтернативу обычным вызовам функции механизм, такой как встроенная подстановка. Встроенная подстановка не текстовая подстановка, а также не создает новую функцию. Следовательно, например, расширение макроса, используемого в теле функция использует определение, которое оно имело в точке тела функции появляется, а не там, где вызывается функция; и идентификаторы к объявлениям в области, где происходит тело. Аналогичным образом, функция имеет один адрес, независимо от количества встроенных определения, которые возникают в дополнение к внешним определение.

139) Например, реализация может никогда не выполняет встроенную подстановку или может выполнять только встроенные подстановки к вызовам в рамках встроенного объявления.

Он предлагает компилятору, что эта функция широко используется и требует предпочтения скорости при вызове этой функции. Но с современным интеллектуальным компилятором это может быть более или менее неуместным, поскольку компиляторы могут решить, должна ли функция быть встроенной и может игнорировать встроенный запрос от пользователей, потому что современные компиляторы могут очень эффективно решать, как вызывать функции.

static inline void f(void) {} не имеет практической разницы с static void f(void) {}.

Так что да с современными компиляторами большую часть времени нет. С любыми компиляторами существуют нет практические/наблюдаемые разности выходных данных.

inline void f(void) {} в C не работает как путь С++. Как это сделать работать в C?

Функция, которая является встроенной в любом месте, должна быть встроена повсюду в С++, а компоновщик не жалуется на ошибку множественного определения (определение должно быть одинаковым).

Что на самом деле делает extern inline void f (void); делать?

Это обеспечит внешнюю привязку к f. Поскольку f может присутствовать в другом блоке компиляции, компилятор может выбрать другой механизм вызова для ускорения вызовов или полностью игнорировать inline.

Ответ 4

Функция, в которой все объявления (включая определение) упоминают inline и никогда не extern.
Должно быть определение в одной и той же единицы перевода. Стандарт относится к этому как встроенное определение.
Не выделяется автономный объектный код, поэтому это определение нельзя вызывать из другой единицы перевода.

В этом примере все объявления и определения используют inline, но не extern:

// a declaration mentioning inline     
inline int max(int a, int b);

// a definition mentioning inline  
inline int max(int a, int b) {  
  return a > b ? a : b;  
}

Здесь - это ссылка, которая может дать вам более ясную информацию о встроенных функциях в C, а также об использовании inline и extern.

Ответ 5

Как слово "Inline" говорит "In" "Line", добавление этого ключевого слова в функцию влияет на программу во время выполнения, когда компиляция программы, функция, написанная внутри кода, вставлена ​​под вызов функции, поскольку вызовы функций более дорогостоящий, чем встроенный код, поэтому это оптимизирует код. Так, static inline void f (void) {} ​​и static void f (void) {}, это ключевое слово inline делает разницу во времени выполнения. Но когда функция имеет слишком много строк кода, это не повлияет на время выполнения. Если вы добавляете статический элемент перед функцией, время жизни функции - это время жизни всей программы. И использование этой функции ограничивается только этим файлом. Чтобы узнать об extern, вы можете ссылаться на Влияние ключевого слова extern на функции C