В чем разница между «статической» и «статической встроенной» функцией?


123

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

В чем разница между статической и статической встроенной функцией?

Почему следует inlineпомещать в файл заголовка, а не в .cфайл?

Ответы:


108

inlineинструктирует компилятор попытаться встроить содержимое функции в вызывающий код вместо выполнения фактического вызова.

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

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

например:

static int Inc(int i) {return i+1};
.... // some code
int i;
.... // some more code
for (i=0; i<999999; i = Inc(i)) {/*do something here*/};

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

 int i;
 ....
 for (i=0; i<999999; i = i+1) { /* do something here */};

Пропуск фактического вызова функции и возврата

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

staticотносится к сфере применения. В языке C это означает, что функция / переменная может использоваться только в одной и той же единице перевода.


4
Нет, staticотносится к сфере применения. В языке C это означает, что функция / переменная может использоваться только в одной и той же единице перевода.
— littleadv

8
Также важно отметить, что код, объявленный как встроенный, принадлежит заголовку, тогда как обычный (не шаблонный) исходный код не может помещаться в заголовки, не вызывая множественных ошибок переопределения. Таким образом, даже при объявлении чего-либо встроенного, даже если компилятор решает не встраивать это, все равно существует стандартное обязательное многократное переопределение, избегающее поведения, которое
— срабатывает

13
@VoidStar На самом деле static(с или без inline) вполне может быть в заголовке, не вижу причин, почему бы и нет. Шаблоны для C ++, этот вопрос о C.
— littleadv

2
@littleadv: основная причина размещения определений функций в файлах заголовков - сделать их встраиваемыми, поэтому их явная маркировка inline- хороший стиль, имо
— Кристоф

10
На самом деле inlineне инструктирует компилятор делать какие-либо попытки встраивания. Это просто позволяет программисту включать тело функции в несколько единиц трансляции без нарушения ODR. Побочным эффектом этого является то, что это дает возможность для компилятора, когда он будет встраивать функцию, на самом деле это сделать.
— Руслан

96

По умолчанию встроенное определение действительно только в текущей единице перевода.

Если класс хранилища равен extern, идентификатор имеет внешнюю связь, а встроенное определение также предоставляет внешнее определение.

Если класс хранения равен static, идентификатор имеет внутреннюю связь, и встроенное определение невидимо в других единицах перевода.

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

Поскольку компилятор может встраивать (и не встраивать) любую функцию, определение которой отображается в текущей единице перевода (и, благодаря оптимизации времени компоновки, даже в разных единицах перевода, хотя стандарт C на самом деле не учитывает that), для большинства практических целей нет разницы между определениями функций staticи static inline.

Спецификатор inline(как и registerкласс хранилища) - это всего лишь подсказка компилятора, и компилятор может полностью ее игнорировать. Неоптимизирующие компиляторы, соответствующие стандартам, должны учитывать только свои побочные эффекты, а оптимизирующие компиляторы будут выполнять эту оптимизацию с явными подсказками или без них.

inlineи registerне бесполезны, тем не менее, поскольку они инструктируют компилятор выдавать ошибки, когда программист пишет код, который сделает оптимизацию невозможной: внешнее inlineопределение не может ссылаться на идентификаторы с внутренней связью (поскольку они были бы недоступны в другой единице перевода) или определить изменяемые локальные переменные со статической продолжительностью хранения (так как они не будут разделять состояние в единицах перевода), и вы не можете брать адреса register-квалифицированных переменных.

Лично я использую соглашение, чтобы отмечать staticопределения функций в заголовках inline, поскольку основная причина помещения определений функций в файлы заголовков - сделать их встраиваемыми.

В общем, я использую только определения static inlineфункций и static constобъектов в дополнение к externобъявлениям в заголовках.

Я никогда не писал inlineфункции с классом хранения, отличным от static.


8
Это правильный ответ. Любой ответ о inlineтом, что он действительно применяется к встраиванию, вводит в заблуждение и, возможно, неверен. Ни один современный компилятор не использует его как подсказку для встраивания или требует его для включения встраивания функции.
— Tyg13

1
проголосовало за «использование соглашения для пометки статических определений функций в заголовках как встроенных».
— Джон З. Ли

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

21

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

Таким образом, я склонен думать, что это staticследует использовать в единицах перевода и извлечь выгоду из дополнительных проверок компилятора для поиска неиспользуемых функций. И static inlineдолжен использоваться в файлах заголовков для предоставления функций, которые могут быть встроены (из-за отсутствия внешней связи) без выдачи предупреждений.

К сожалению, я не могу найти никаких доказательств этой логики. Даже из документации GCC я не смог сделать вывод, что inlineблокирует предупреждения о неиспользуемых функциях. Буду признателен, если кто-нибудь поделится ссылками на описание этого.


1
Ммм, все еще есть warning: unused function 'function' [clang-diagnostic-unused-function]для static inlineфункции при сборке clang-tidy(v8.0.1), который используется в другой единице трансляции. Но определенно, это одно из лучших объяснений и поводов для объединения static& inline!
— DrumM

6

В языке C это staticозначает, что определяемая вами функция или переменная может использоваться только в этом файле (то есть в модуле компиляции).

Итак, это static inlineозначает встроенную функцию, которая может использоваться только в этом файле.

РЕДАКТИРОВАТЬ:

Единица компиляции должна быть единицей перевода


2
Или модными словами: он имеет внутреннюю связь.
— K-Ballo

@AlokSave: Есть ли разница между единицы компиляции и единицами перевода ? Если да, то что более уместно в контексте языка C ++?
— legends2k

Я полагаю the compile unit, что это что-то, что я написал по ошибке, такого не существует, фактическая терминологияtranslation unit
— shengy

Ваш ответ неполный, потому что он в основном используется в файлах заголовков в единицах перевода.
— DrumM

5

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


А как насчет использования inlineв определении? Вы также подразумеваете, что не используете его для externфункций?
— new_perl

Это все еще верно для последней версии GCC? Ваш ответ был бы намного интереснее, если бы вы привели пример и перечислили, какая версия GCC это делает.
— Z-бозон

@Zboson: У меня нет этой информации в свободном доступе, и в данный момент у меня нет времени на настройку и тестирование множества версий gcc, но я согласен, что было бы полезно иметь эту информацию. Вероятно, вы могли бы узнать, когда gcc впервые начал оптимизацию неиспользуемых статических функций / объектов, просмотрев историю attribute((used))и их использование, чтобы позволить asm ссылаться на staticфункции и данные, на которые в противном случае не было бы ссылок .
— R .. GitHub НЕ ПОМОГАЕТ ICE
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.