Що робить екстерн вбудований?


93

Я розумію, що inlineсам по собі є пропозицією для компілятора, і на свій розсуд він може вбудовувати функцію або не вбудовувати її, а також створюватиме об’єктний код, що зв’язується.

Я думаю, що static inlineробить те саме (може, а може і не вбудовано), але не створює прив’язуваний об’єктний код, коли вбудований (оскільки жоден інший модуль не може зв’язуватися з ним).

Де extern inlineвписується малюнок?

Припустимо, я хочу замінити макрос препроцесора на вбудовану функцію і вимагати, щоб ця функція вбудовувалася (наприклад, тому, що вона використовує макроси __FILE__та і __LINE__макроси, які повинні вирішувати для абонента, але не цю функцію, що викликається). Тобто, я хочу побачити помилку компілятора або компонувальника, якщо функція не буде вбудована. Чи extern inlineробить це? (Я припускаю, що, якщо цього не сталося, немає іншого способу досягти цієї поведінки, крім дотримання макросу.)

Чи є відмінності між C ++ та C?

Чи існують відмінності між різними постачальниками компіляторів та версіями?

Відповіді:


129

у K&R C або C89 вбудована мова не входила до мови. Багато компіляторів застосовували його як розширення, але не було визначеної семантики щодо того, як це працювало. GCC був одним з перших , щоб здійснити вбудовування та введений inline, static inlineі extern inlineконструкції; більшість компіляторів до C99, як правило, дотримуються його прикладів.

GNU89:

  • inline: функція може бути вбудованою (хоча це лише підказка). Позаштатна версія завжди видається та видно зовні. Отже, такий вбудований текст можна визначити лише в одному модулі компіляції, а кожен інший повинен розглядати його як функцію, що не працює (або ви отримаєте повторювані символи під час зв’язку).
  • extern inline не буде генерувати позалінійну версію, але може викликати її (яку ви, отже, повинні визначити в іншому модулі компіляції. Однак застосовується правило одного визначення; версія поза рядком повинна мати той самий код, що і inline, пропонований тут, на випадок, якщо компілятор викличе це.
  • static inlineне буде генерувати зовнішню видиму позалінійну версію, хоча вона може генерувати статичну версію файлу. Правило одного визначення не застосовується, оскільки ніколи не існує випромінюваного зовнішнього символу, ні виклику до нього.

C99 (або GNU99):

  • inline: як GNU89 "зовнішній рядок"; жодна видима зовні функція не випромінюється, але її можна викликати, і вона повинна існувати
  • extern inline: як GNU89 "вбудований": видається зовнішньо видимий код, тому щонайбільше одна одиниця перекладу може використовувати це.
  • static inline: як GNU89 "статичний вбудований". Це єдиний портативний пристрій між gnu89 та c99

C ++:

Функція, яка є вбудованою де завгодно, повинна бути вбудованою скрізь, з однаковим визначенням. Компілятор / компонувальник сортує кілька екземплярів символу. Не існує визначення static inlineабо extern inline, хоча вони є у багатьох компіляторів (як правило, дотримуючись моделі gnu89).


2
У класичному класичному C "вбудований" не був ключовим словом; він був доступний для використання як ім'я змінної. Це стосується C89 та попереднього стандарту (K&R) C.
Джонатан Леффлер

Здається, ти маєш рацію. Виправлено. Я думав, що це було зарезервовано як ключове слово в C89 (хоча не в K&R), але, здається, я неправильно запам'ятав
puetzk

Я хотів би додати, що для Visual C ++ від Microsoft існує ключове слово __forceinline, яке забезпечить вбудовування вашої функції. Очевидно, що це розширення для компілятора лише для VC ++.
без

Чи є якась різниця між C99 "зовнішній вбудований" та відсутність специфікаторів взагалі?
Джо Со

Семантично ні; так само, як не вбудована функція, extern inlineпідпорядковується одному правилу визначення, і це визначення. Але якщо визначена реалізацією евристика оптимізації дотримується рекомендації використовувати inlineключове слово як пропозицію, що "виклики функції виконуються якомога швидше" (ISO 9899: 1999 §6.7.4 (5), extern inlineпідрахунок
puetzk,

31

Я вважаю, ви неправильно розумієте __FILE__ та __LINE__ на основі цього твердження:

тому що він використовує макроси __FILE__ та __LINE__, які повинні вирішити для абонента, але не цю функцію, що викликається

Існує кілька фаз компіляції, і попередньою обробкою є перша. __FILE__ та __LINE__ замінюються на цьому етапі. Отже, до того часу, коли компілятор може розглянути функцію вбудовування, вони вже були замінені.


14

Здається, ви намагаєтеся написати щось подібне:

inline void printLocation()
{
  cout <<"You're at " __FILE__ ", line number" __LINE__;
}

{
...
  printLocation();
...
  printLocation();
...
  printLocation();

і сподіваючись, що ви будете отримувати різні значення кожного разу. Як говорить Дон, ви цього не зробите, тому що __FILE__ та __LINE__ реалізовані препроцесором, але вбудований реалізований компілятором. Отже, куди б ви не зателефонували з printLocation, ви отримаєте однаковий результат.

Тільки спосіб , яким Ви можете отримати цю роботу, щоб зробити printLocation макрос. (Так, я знаю...)

#define PRINT_LOCATION  {cout <<"You're at " __FILE__ ", line number" __LINE__}

...
  PRINT_LOCATION;
...
  PRINT_LOCATION;
...

18
Загальна хитрість полягає у тому, щоб макрос PRINT_LOCATION викликав функцію printLocation, передаючи FILE та LINE як параметри. Це може призвести до кращої поведінки налагоджувача / редактора / тощо, коли тіло функції нетривіальне.
Стів Джессоп,

@Roddy Подивіться на моє рішення - ваше розширення, але більш повне та розширюване.
enthusiasticgeek

@SteveJessop Щось подібне я перелічив у наведеному нижче рішенні?
enthusiasticgeek

3

Ситуація з вбудованим, статичним вбудованим та зовнішнім вбудованим є складною, не в останню чергу тому, що gcc та C99 визначають дещо різні значення для своєї поведінки (і, мабуть, також C ++). Ви можете знайти корисну та детальну інформацію про те, що вони роблять в C тут .


2

Тут ви обираєте макроси, а не вбудовані функції. Рідкісний випадок, коли макроси керують вбудованими функціями. Спробуйте наступне: Я написав цей "MACRO MAGIC" код, і він повинен працювати! Перевірено на gcc / g ++ Ubuntu 10.04

//(c) 2012 enthusiasticgeek (LOGGING example for StackOverflow)

#ifdef __cplusplus

#include <cstdio>
#include <cstring>

#else

#include <stdio.h>
#include <string.h>

#endif

//=========== MACRO MAGIC BEGINS ============

//Trim full file path
#define __SFILE__ (strrchr(__FILE__,'/') ? strrchr(__FILE__,'/')+1 : __FILE__ )

#define STRINGIFY_N(x) #x
#define TOSTRING_N(x) STRINGIFY_N(x)
#define _LINE (TOSTRING_N(__LINE__))

#define LOG(x, s...) printf("(%s:%s:%s)"  x "\n" , __SFILE__, __func__, _LINE, ## s);

//=========== MACRO MAGIC ENDS ============

int main (int argc, char** argv) {

  LOG("Greetings StackOverflow! - from enthusiasticgeek\n");

  return 0;
}

Для кількох файлів визначте ці макроси в окремому файлі заголовка, включаючи однакові в кожному файлі c / cc / cxx / cpp. Будь ласка, віддайте перевагу вбудованим функціям або ідентифікаторам const (залежно від випадку) над макросами, де це можливо.


2

Замість відповіді "що це робить?", Я відповідаю "як змусити робити те, що я хочу?" Існує 5 видів вкладання, всі доступні в GNU C89, стандарт C99 і C ++:

завжди вбудований, якщо не взята адреса

Додайте __attribute__((always_inline))до будь-якої декларації, а потім використовуйте один із наведених нижче випадків, щоб обробити можливість прийняття її адреси.

Ймовірно, вам ніколи не слід використовувати це, якщо вам не потрібна його семантика (наприклад, щоб впливати на збірку певним чином або використовувати alloca). Зазвичай компілятор краще за вас знає, чи варто це робити.

вбудований і видає слабкий символ (наприклад, C ++, він же "просто змусити його працювати")

__attribute__((weak))
void foo(void);
inline void foo(void) { ... }

Зауважте, що це залишає купу копій одного і того ж коду, і компонувальник вибирає одну довільно.

вбудований, але ніколи не видає жодного символу (залишаючи зовнішні посилання)

__attribute__((gnu_inline))
extern inline void foo(void) { ... }

випромінювати завжди (для одного TU, щоб вирішити попереднє)

Натякана версія видає слабкий символ на C ++, але сильний символ на будь-якому з діалектів C:

void foo(void);
inline void foo(void) { ... }

Або ви можете зробити це без підказки, яка видає сильний символ на обох мовах:

void foo(void) { ... }

Як правило, ви знаєте, якою мовою є ваша ТУ, коли ви надаєте визначення, і, мабуть, вам не потрібно багато вкладати.

вбудовані та випромінюють у кожному TU

static inline void foo(void) { ... }

Для всіх перерахованих, крім staticодного, ви можете додати avoid foo(void) заяву вище. Це допомагає з "найкращою практикою" написання чистих заголовків, а потім #includeстворення окремого файлу із вбудованими визначеннями. Потім, якщо використовується вбудований стиль C, #defineдеякі макроси по-різному в одному виділеному TU надають позарядкові визначення.

Не забувайте, extern "C"чи можна використовувати заголовок як з C, так і з C ++!


Відсутнє: а як щодо MSVC? У ньому є деякі розширення діалекту C89, але я ніколи не використовую MSVC і не знаю, як запустити його nmеквівалент.
o11c
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.