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

123

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

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

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

new_perl
источник

Ответы:

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 это означает, что функция / переменная может использоваться только в одной и той же единице перевода.

littleadv
источник
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.

Christoph
источник
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означает встроенную функцию, которая может использоваться только в этом файле.

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

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

shengy
источник
2
Или модными словами: он имеет внутреннюю связь.
K-Ballo
@AlokSave: Есть ли разница между единицы компиляции и единицами перевода ? Если да, то что более уместно в контексте языка C ++?
legends2k
Я полагаю the compile unit, что это что-то, что я написал по ошибке, такого не существует, фактическая терминологияtranslation unit
shengy
Ваш ответ неполный, потому что он в основном используется в файлах заголовков в единицах перевода.
DrumM
5

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

R .. GitHub НЕ ПОМОГАЕТ ICE
источник
А как насчет использования inlineв определении? Вы также подразумеваете, что не используете его для externфункций?
new_perl
Это все еще верно для последней версии GCC? Ваш ответ был бы намного интереснее, если бы вы привели пример и перечислили, какая версия GCC это делает.
Z-бозон
@Zboson: У меня нет этой информации в свободном доступе, и в данный момент у меня нет времени на настройку и тестирование множества версий gcc, но я согласен, что было бы полезно иметь эту информацию. Вероятно, вы могли бы узнать, когда gcc впервые начал оптимизацию неиспользуемых статических функций / объектов, просмотрев историю attribute((used))и их использование, чтобы позволить asm ссылаться на staticфункции и данные, на которые в противном случае не было бы ссылок .
R .. GitHub НЕ ПОМОГАЕТ ICE