Що таке зовнішня та внутрішня зв'язки?


337

Я хочу зрозуміти зовнішні та внутрішні зв’язки та їхню різницю.

Я також хочу знати значення

constзмінні внутрішньо посилаються за замовчуванням, якщо інше не оголошено як extern.

Відповіді:


279

При записі файлу реалізації ( .cpp, .cxxі т.д.) , компілятор генерує одиницю перекладу . Це вихідний файл вашої реалізації, а також усі заголовки, які ви будете мати #includeв ньому.

Внутрішня зв'язок стосується всього лише сфери перекладу .

Зовнішня посилання стосується речей, які існують поза певною одиницею перекладу. Іншими словами, доступний через всю програму , яка є комбінацією всіх перекладацьких одиниць (або об’єктних файлів).


112
Я б схвалив це за винятком одного глюку: Блок перекладу не "якось об'єктний файл", це вихідний код, з якого компілятор створює об'єктний файл.
sbi

4
@FrankHB, що таке "щось важливіше", що відповідь відсутня?
Математик

2
@Mathematician Вибачте за запізнення ... Я думаю, що проблема повинна бути очевидною (крім точності формулювання). Ця відповідь є неповною, оскільки питання про правило constзмінних (як і його призначення) тут повністю пропущено.
FrankHB

293

Як зазначає dudewat, зовнішня зв'язок означає, що символ (функція або глобальна змінна) є доступним у всій вашій програмі, а внутрішній зв'язок означає, що він доступний лише в одному блоці перекладу .

Ви можете чітко керувати зв'язком символу, використовуючи externі staticключові слова. Якщо зв'язок не вказаний, то за замовчуванням зв'язок призначений externдля несимволів constта static(внутрішніх) constсимволів.

// in namespace or global scope
int i; // extern by default
const int ci; // static by default
extern const int eci; // explicitly extern
static int si; // explicitly static

// the same goes for functions (but there are no const functions)
int foo(); // extern by default
static int bar(); // explicitly static 

Зауважте, що замість staticвнутрішніх зв'язків краще використовувати анонімні простори імен, до яких також можна помістити classes. Зв'язок для анонімних просторів імен змінився між C ++ 98 та C ++ 11, але головне - вони недоступні для інших перекладацьких одиниць.

namespace {
   int i; // external linkage but unreachable from other translation units.
   class invisible_to_others { };
}

11
Реалізація ключового слова "експорт" підкреслила різницю між функцією, оголошеною "статичною", і функцією, оголошеною в неназваному просторі імен. Підсумовуючи якнайкраще, шаблон функції, оголошений ключовим словом експорту в одному блоці перекладу, може посилатися на функцію, визначену в неназваному просторі імен іншого блоку перекладу в результаті двофазного пошуку. ( ddj.com/showArticle.jhtml?articleID=184401584 )
Річард Корден

Що робити, якщо я роблю наступне: 1.cpp <code> const int ci; </code> 2.cpp <code> extern const int ci; </code>
Rajendra Uppal,

2
@Rajenda ви отримаєте невирішену помилку символу (вибачте за дев'ять місяців затримки у відповіді, що я пропустив цей коментар).
Мотті

4
Інформація, яка може значно покращити цю відповідь: 1) статична статистика більше не застаріла в C ++ 11. 2) анонімні члени простору імен у C ++ 11 мають за замовчуванням внутрішню зв'язок. Дивіться stackoverflow.com/questions/10832940/…
Клайм

2
Що це означає "зовнішній зв'язок, але недоступний для інших перекладацьких одиниць"? Як воно може бути недосяжним, але все ж зовнішнім?
szx

101
  • Глобальна змінна за замовчуванням має зовнішню зв'язок . Її обсяг може бути розширений на інші файли, ніж їх містити, даючи відповідне externоголошення в іншому файлі.
  • Область глобальної змінної може бути обмежена файлом, що містить її декларацію, префіксуючи декларацію ключовим словом static. Кажуть, що такі змінні мають внутрішній зв'язок .

Розглянемо наступний приклад:

1.cpp

void f(int i);
extern const int max = 10;
int n = 0;
int main()
{
    int a;
    //...
    f(a);
    //...
    f(a);
    //...
}
  1. Підпис функції fоголошується fфункцією із зовнішнім зв’язком (за замовчуванням). Його визначення повинно бути надано пізніше у цьому файлі чи в іншому підрозділі перекладу (подано нижче).
  2. maxвизначається як ціла константа. За замовчуванням зв'язок для констант є внутрішньою . Його зв'язок змінюється на зовнішню за допомогою ключового слова extern. Тож тепер maxможна отримати доступ до інших файлів.
  3. nвизначається як ціла змінна. Зв'язок за замовчуванням для змінних, визначених поза тілами функцій, є зовнішнім .

2.cpp

#include <iostream>
using namespace std;

extern const int max;
extern int n;
static float z = 0.0;

void f(int i)
{
    static int nCall = 0;
    int a;
    //...
    nCall++;
    n++;
    //...
    a = max * z;
    //...
    cout << "f() called " << nCall << " times." << endl;
}
  1. maxоголошено, що має зовнішню зв'язок . У maxдеякому файлі повинно з’явитися відповідне визначення для (із зовнішньою зв'язком). (Як у 1.cpp)
  2. nоголошено, що має зовнішню зв'язок .
  3. zбуде визначена як глобальна змінна з внутрішнім зв'язком .
  4. Визначення nCallвказує nCallна змінну, яка зберігає своє значення для викликів до функції f(). На відміну від локальних змінних з класом автоматичного зберігання за замовчуванням, nCallбуде ініціалізовано лише один раз на початку програми, а не один раз для кожного виклику f(). Специфікатор класу зберігання staticвпливає на термін служби локальної змінної, а не на її обсяг.

Примітка: ключове слово staticвідіграє подвійну роль. При використанні у визначеннях глобальних змінних він визначає внутрішню взаємозв'язок . При використанні у визначеннях локальних змінних він вказує, що тривалість роботи змінної буде тривалістю програми, а не тривалістю функції.

Сподіваюся, що це допомагає!


2
Важливо, що при використанні у визначеннях локальних змінних staticдозволяє ледача одиночна ініціалізація (яка може бути корисною, якщо вам потрібен глобальний ish об'єкт, але повинна контролювати, коли вона побудована через проблеми із глобальним порядком побудови та не може динамічно розподілити її використання в newтой час як більш поглиблені схеми ініціалізації можуть бути вищими за рамки необхідних для відповідного об'єкта; за наслідком це, головним чином, проблема вбудованих систем, які використовують C ++).
JAB

1
Дуже хороший іспит, зробив мій день.
Blood-HaZaRd

28

У термінах "C" (оскільки статичне ключове слово має різне значення між "C" і "C ++")

Давайте поговоримо про різну сферу застосування у "C"

ОБЛАСТЬ: В основному, як довго я щось бачу і як далеко.

  1. Локальна змінна: Область застосування лише всередині функції. Він знаходиться в області STACK в оперативній пам'яті. Що означає, що кожного разу, коли функція отримує виклик, всі змінні, що входять до складу цієї функції, включаючи аргументи функції, щойно створюються та знищуються, коли контроль виходить із функції. (Оскільки стек розмивається щоразу, коли функція повертається)

  2. Статична змінна: Обсяг цього стосується файлу. Він доступний скрізь у файлі,
    в якому він оголошений. Він знаходиться в сегменті оперативної пам'яті DATA. Оскільки це можна отримати лише у файлі, а значить, і ВНУТРІШНЕ посилання. Будь-які
    інші файли не можуть бачити цю змінну. Насправді ключове слово STATIC - це єдиний спосіб, коли ми можемо ввести певний рівень даних або функцій, що
    ховаються у "C"

  3. Глобальна змінна: Обсяг цього стосується цілої програми. Це доступна форма у будь-якому місці програми. Глобальні змінні також розміщені в сегменті DATA, оскільки до нього можна отримати доступ будь-де в додатку, а значить і ЗОВНІШНЕ ЗВ'ЯЗКУ

За замовчуванням усі функції є глобальними. У випадку, якщо вам потрібно приховати деякі функції у файлі ззовні, ви можете встановити статичне ключове слово до функції. :-)


12
@Libin: Що стосується 1) локальні змінні не повинні бути в стеці - вони зазвичай знаходяться в стеці, але можуть бути в регістрах і в середовищі ARM, вони частіше в регістрах, ніж в стеці (залежить від деяких факторів - рівня виклику, кількості формальних арг ..)
Артур

4
@Libin: Що стосується 1) Якщо ви розглядаєте "flush" як перезапис - це неправильно. Покажчик стека просто переміщується в інше місце. Ніякі "раніше діючі локальні варі" не змиваються / очищаються тощо. Ви змішуєте змінний обсяг із тривалістю зберігання. Область застосування повідомляє, звідки ви можете отримати доступ до вар. Тривалість зберігання говорить про те, як довго воно існує. Ви можете мати локальну змінну зі статичною тривалістю зберігання. Це означає, що воно живе "вічно", але отримати доступ до нього можна з функції, про яку було заявлено.
Артур

2
Зрозуміло для неточних понять і очевидних помилок. Строго кажучи, немає "глобальної" і "змінної" (як іменник), визначеної в C. Можливо, ви, мабуть, хочете посилатися на "об'єкт файлового об'єму", а не на "глобальну змінну", але говорити про "область" (в C it є властивістю ідентифікатора ) це нісенітниця. (Обидва терміни визначені в С ++ нормативно з дещо різними значеннями.)
FrankHB

@Artur Я думаю, що ви забули " єдине " в " Це означає, що воно живе" вічно ", але до нього можна отримати доступ (лише) з функції, про яку заявлено. " - Це важлива деталь, тому я хотів би зазначити це прямо.
RobertS підтримує Моніку Селліо

14

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

Наголошу на деяких ключових моментах, особливо ті, які відсутні у попередніх відповідях.

Зв'язок - це властивість імені , яке вводиться декларацією . Різні імена можуть позначати одну сутність (як правило, об'єкт або функцію). Тому говорити про зв'язок суб'єкта господарювання, як правило, нісенітниця, якщо ви не впевнені, що суб'єкт господарювання буде посилатися лише унікальним іменем у деяких конкретних деклараціях (однак, як правило, одна декларація).

Зауважте, що об'єкт - це сутність, але змінна - ні. Говорячи про зв'язок змінної, насправді йдеться про назву позначеної сутності (яка вводиться конкретною декларацією). Зв'язок імені є одним із трьох: відсутність зв'язків, внутрішніх зв'язків чи зовнішніх зв'язків.

Різні одиниці перекладу можуть надавати одне і те ж декларація за заголовком / вихідним файлом (так, це формулювання стандарту). Тож ви можете вказати те саме ім’я в різних підрозділах перекладу. Якщо назва, яке оголошує, має зовнішню зв'язок, то ідентифікатор особи, на яку посилається ім'я, також поділяється. Якщо назва, яке оголошує, має внутрішню зв'язок, те саме ім'я в різних одиницях перекладу позначає різні сутності, але ви можете вказати сутність у різних областях однієї одиниці перекладу. Якщо назва не має зв’язків, ви просто не можете передати об'єкт з інших областей.

(На жаль, я знайшов, що я набрав, дещо просто повторював стандартне формулювання ...)

Існують також деякі інші заплутані моменти, які не охоплені специфікацією мови.

  1. Видимість (імені). Це також властивість оголошеного імені, але зі значенням, відмінним від зв'язку .
  2. Видимість (побічного ефекту) . Це не пов’язано з цією темою.
  3. Видимість (символу). Це поняття може використовуватися фактичними реалізаціями . У таких реалізаціях символ із специфічною видимістю в об'єктному (бінарному) коді зазвичай є цільовим, відображеним із визначення сутності, імена якого мають однакову специфічну зв'язок у вихідному (C ++) коді. Однак, як правило, не гарантується один на один. Наприклад, символ у динамічному зображенні бібліотеки може бути вказаний лише загальнодоступним для цього зображення внутрішньо з вихідного коду (задіяний з деякими розширеннями, як правило, __attribute__або__declspec) або параметри компілятора, і зображення - це не вся програма чи об'єктний файл, перекладений з блоку перекладу, тому жодна стандартна концепція не може точно описати це. Оскільки символ не є нормативним терміном в C ++, він є лише деталізацією реалізації, хоча пов'язані розширення діалектів, можливо, були широко прийняті.
  4. Доступність. У C ++ зазвичай йдеться про властивості членів класу або базових класів , що знову ж таки є іншим поняттям, не пов’язаним із темою.
  5. Глобальний. У C ++ "глобальний" позначає щось із глобального простору імен або загальної області простору імен. Останній приблизно еквівалентний обсягу файлів мовою С. І в C, і в C ++ посилання не має нічого спільного з сферою застосування, хоча сфера (як і зв'язок) також щільно пов'язана з ідентифікатором (в C) або іменем (в C ++), введеним деякою декларацією.

Правило зв'язування області простору імен constзмінного щось особливе (і в Зокрема , відрізняється від constоб'єкта , оголошеного в області видимості файлу в C мовою , який також має концепцію зв'язування ідентифікаторів). Оскільки ODR застосовується за допомогою C ++, важливо зберігати не більше одного визначення тієї самої змінної або функції, що відбулися у всій програмі, за винятком inlineфункцій . Якщо немає такого спеціального правила const, найпростіше оголошення constзмінної з ініціалізаторами (наприклад = xxx) у заголовку чи вихідному файлі (часто "файл заголовка"), включеному декількома одиницями перекладу (або включеними однією одиницею перекладу більше, ніж один раз, хоча рідко) в програмі буде порушувати ODR, який змушує використовуватиconst змінна як заміна деяких об’єктоподібних макросів неможлива.


3
Ця відповідь звучить дуже кваліфіковано і може бути дуже точною (я не можу судити про це), але, швидше за все, вона не настільки зрозуміла, як цього хочуть багато людей, які шукають це питання замість того, щоб безпосередньо читати мовні характеристики. Принаймні для моїх потреб я буду дотримуватися прийнятої відповіді, але все-таки дякую за те, що ви дали невелике уявлення про мовні характеристики. 👍🏻
WEDI

8

Я думаю, що внутрішні та зовнішні зв’язки в C ++ дають чітке і стисле пояснення:

Блок перекладу посилається на файл реалізації (.c / .cpp) та всі файли заголовка (.h / .hpp), які він включає. Якщо об'єкт або функція всередині такої одиниці перекладу має внутрішню зв'язок, то цей специфічний символ видно тільки для лінкера в межах цього блоку перекладу. Якщо об'єкт або функція має зовнішній зв'язок, лінкер також може бачити його під час обробки інших одиниць перекладу. Статичне ключове слово при використанні в глобальному просторі імен змушує символ мати внутрішню зв'язок. Ключове слово Extern приводить до символу, що має зовнішню зв'язок.

Компілятор за замовчуванням пов'язує символи таким чином:

Глобальні змінні non-const мають за замовчуванням зовнішні зв'язки
Const глобальні змінні мають внутрішню зв'язок за замовчуванням
Функції мають зовнішню зв'язок за замовчуванням


6

Зв'язок визначає, чи ідентифікатори, що мають однакові імена, посилаються на один і той же об'єкт, функцію чи іншу сутність, навіть якщо ці ідентифікатори відображаються в різних одиницях перекладу. Зв'язок ідентифікатора залежить від того, як він був оголошений. Існує три типи зв’язків:

  1. Внутрішня зв'язок : ідентифікатори можна побачити лише в блоці перекладу.
  2. Зовнішня зв'язок : ідентифікатори можна побачити (і посилатися на них) в інших одиницях перекладу.
  3. Ніяких зв’язків : ідентифікатори можна побачити лише в тій області, в якій вони визначені. Зв'язок не впливає на масштабування

Тільки C ++ : Ви також можете мати зв'язок між C ++ та фрагментами коду, що не належить C ++, що називається мовним зв'язком .

Джерело: Зв'язок програми IBM


5

В основному

  • extern linkage змінна видно у всіх файлах
  • internal linkage змінна видно в одному файлі.

Поясніть: змінні const внутрішньо посилаються за замовчуванням, якщо інше не оголошено зовнішнім

  1. за замовчуванням глобальна змінна - external linkage
  2. але constглобальна змінна єinternal linkage
  3. додатково, extern constглобальна змінна єexternal linkage

Досить хороший матеріал про зв’язок у C ++

http://www.goldsborough.me/c/c++/linker/2016/03/30/19-34-25-internal_and_external_linkage_in_c++/


1

В C ++

Будь-яка змінна в області файлу, яка не вкладається в клас або функцію, видно в усіх одиницях перекладу програми. Це називається зовнішнім зв’язком, тому що під час посилання ім'я видно посилання, що знаходиться повсюдно, зовнішнє для цього блоку перекладу.

Глобальні змінні та звичайні функції мають зовнішній зв'язок.

Статичне ім'я об'єкта або функції в області файлу є локальним для блоку перекладу. Це називається внутрішньою зв'язком

Посилання стосується лише елементів, які мають адреси у часі посилання / завантаження; таким чином, оголошення класів та локальні змінні не мають зв'язку.


const global vars мають внутрішню зв'язок.
Blood-HaZaRd
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.