Co to jest powiązanie zewnętrzne i wewnętrzne?


337

Chcę zrozumieć powiązanie zewnętrzne i wewnętrzne oraz ich różnicę.

Chcę też poznać znaczenie

constzmienne są wewnętrznie połączone domyślnie, chyba że zadeklarowano inaczej jako extern.

Odpowiedzi:


279

Kiedy piszesz plik realizacji ( .cpp, .cxxitp) kompilator generuje jednostkę tłumaczeniową . To jest plik źródłowy z twojej implementacji plus wszystkie nagłówki #includew nim zawarte.

Powiązanie wewnętrzne odnosi się do wszystkiego tylko w zakresie jednostki tłumaczeniowej .

Zewnętrzne powiązanie odnosi się do rzeczy, które istnieją poza określoną jednostką tłumaczeniową. Innymi słowy, dostępny przez cały program , który jest kombinacją wszystkich jednostek tłumaczeniowych (lub plików obiektowych).


112
Głosowałbym za tym z wyjątkiem jednej usterki: jednostka tłumacząca nie jest „jakimś plikiem obiektowym”, to kod źródłowy, z którego kompilator tworzy plik obiektowy.
sbi

4
@FrankHB, jakie jest „coś ważniejszego”, że brakuje odpowiedzi?
Matematyk

2
@ Matematyk Przepraszam za spóźnienie ... Myślę, że problem powinien być oczywisty (oprócz dokładności sformułowania). Ta odpowiedź jest niepełna, ponieważ pytanie o zasadę constzmiennych (a także o jej cel) jest tutaj całkowicie pominięte.
FrankHB,

293

Jak powiedział dudewat, zewnętrzne połączenie oznacza, że ​​symbol (funkcja lub zmienna globalna) jest dostępny w całym programie, a wewnętrzne połączenie oznacza, że ​​jest dostępne tylko w jednej jednostce tłumaczeniowej .

Możesz jawnie kontrolować powiązanie symbolu za pomocą słów kluczowych externi static. Jeśli połączenie nie jest określone, wówczas domyślne połączenie jest externdla constsymboli niebędących symbolami i static(wewnętrzne) dla constsymboli.

// 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 

Zauważ, że zamiast używać staticwewnętrznego połączenia, lepiej jest używać anonimowych przestrzeni nazw, w których możesz również umieścić classes. Łączenie anonimowych przestrzeni nazw zmieniło się między C ++ 98 i C ++ 11, ale najważniejsze jest to, że są one nieosiągalne dla innych jednostek tłumaczeniowych.

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

11
Implementacja słowa kluczowego „eksport” uwidoczniła różnicę między funkcją zadeklarowaną jako „statyczną” a funkcją zadeklarowaną w przestrzeni nazw bez nazwy. Podsumowując najlepiej, jak potrafię, szablon funkcji zadeklarowany za pomocą słowa kluczowego eksportu w jednej jednostce tłumaczeniowej może odnosić się do funkcji zdefiniowanej w nienazwanej przestrzeni nazw innej jednostki tłumaczeniowej w wyniku wyszukiwania 2-fazowego. ( ddj.com/showArticle.jhtml?articleID=184401584 )
Richard Corden

Co jeśli zrobię: 1.cpp <code> const int ci; </code> 2.cpp <code> extern const int ci; </code>
Rajendra Uppal

2
@Rajenda pojawi się nierozwiązany błąd symbolu (przepraszam za dziewięciomiesięczne opóźnienie w odpowiedzi, że przegapiłem ten komentarz).
Motti

4
Informacje, które mogą znacznie poprawić tę odpowiedź: 1) static nie jest już przestarzałe w C ++ 11. 2) anonimowi członkowie przestrzeni nazw w C ++ 11 mają domyślnie wewnętrzne powiązanie. Zobacz stackoverflow.com/questions/10832940/...
Klaim,

2
Co to znaczy „zewnętrzny link, ale nieosiągalny z innych jednostek tłumaczeniowych”? Jak może być nieosiągalny, ale wciąż zewnętrzny?
szx

101
  • Zmienna globalna ma domyślnie powiązanie zewnętrzne . Jego zakres można rozszerzyć na pliki inne niż zawierające go poprzez dopasowanieextern deklarację w drugim pliku.
  • Zakres zmiennej globalnej można ograniczyć do pliku zawierającego jego deklarację, poprzedzając deklarację słowem kluczowym static. Mówi się, że takie zmienne mają wewnętrzne powiązanie .

Rozważ następujący przykład:

1. cpp

void f(int i);
extern const int max = 10;
int n = 0;
int main()
{
    int a;
    //...
    f(a);
    //...
    f(a);
    //...
}
  1. Podpis funkcji fdeklaruje się fjako funkcję z zewnętrznym łącznikiem (domyślnie). Jego definicja musi zostać podana w dalszej części tego pliku lub w innej jednostce tłumaczeniowej (podanej poniżej).
  2. maxjest zdefiniowany jako stała całkowita. Domyślne połączenie dla stałych jest wewnętrzne . Jego powiązanie zostało zmienione na zewnętrzne za pomocą słowa kluczowego extern. Teraz maxmożna uzyskać dostęp do innych plików.
  3. njest zdefiniowany jako zmienna całkowita. Domyślne powiązanie dla zmiennych zdefiniowanych poza ciałami funkcji jest zewnętrzne .

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. maxjest zadeklarowany jako zewnętrzny link . W maxjakimś pliku musi pojawić się pasująca definicja (z zewnętrznym powiązaniem). (Jak w 1.cpp)
  2. njest zadeklarowany jako zewnętrzny link .
  3. zjest zdefiniowany jako zmienna globalna z wewnętrznym sprzężeniem .
  4. Definicja nCallokreśla, nCallże jest zmienną, która zachowuje swoją wartość między wywołaniami funkcji f(). W przeciwieństwie do zmiennych lokalnych z domyślną automatyczną klasą pamięci, nCallbędą inicjowane tylko raz na początku programu, a nie raz dla każdego wywołania f(). Specyfikator klasy pamięci staticwpływa na czas życia zmiennej lokalnej, a nie na jej zakres.

Uwaga: słowo kluczowe staticodgrywa podwójną rolę. W przypadku definicji zmiennych globalnych określa powiązanie wewnętrzne . Kiedy jest używany w definicjach zmiennych lokalnych, określa, że ​​czas życia zmiennej ma być czasem trwania programu, a nie czasem trwania funkcji.

Mam nadzieję, że to pomaga!


2
Co ważne, gdy jest używany w definicjach zmiennych lokalnych, staticpozwala na leniwe pojedyncze inicjowanie (co może być przydatne, jeśli potrzebujesz obiektu globalnego, ale musisz kontrolować, kiedy jest on budowany z powodu problemów z globalnym porządkiem konstrukcji i nie może dynamicznie przydzielić go używanie newbardziej szczegółowych schematów inicjalizacji może wykraczać poza to, co jest konieczne dla danego obiektu; w konsekwencji jest to głównie problem w systemach wbudowanych korzystających z C ++).
JAB

1
Bardzo dobry egzamin, sprawił, że mój dzień.
Blood-HaZaRd

28

Pod względem „C” (ponieważ słowo kluczowe ma inne znaczenie między „C” i „C ++”)

Porozmawiajmy o innym zakresie w „C”

ZAKRES: Zasadniczo to jak długo mogę coś zobaczyć i jak daleko.

  1. Zmienna lokalna: Zakres jest tylko wewnątrz funkcji. Znajduje się w obszarze STOSOWANIA RAM. Co oznacza, że ​​za każdym razem, gdy funkcja jest wywoływana, wszystkie zmienne wchodzące w skład tej funkcji, w tym argumenty funkcji, są tworzone na nowo i niszczone, gdy kontrola wyjdzie z funkcji. (Ponieważ stos jest opróżniany za każdym razem, gdy funkcja powraca)

  2. Zmienna statyczna: zakres tego dotyczy pliku. Jest dostępny w każdym miejscu pliku,
    w którym został zadeklarowany. Znajduje się w segmencie danych RAM. Ponieważ można to uzyskać tylko w pliku, a tym samym połączenie wewnętrzne. Każdy
    inne pliki nie widzą tej zmiennej. W rzeczywistości słowo kluczowe STATIC jest jedynym sposobem, w jaki możemy wprowadzić pewien poziom danych lub funkcji
    ukrywających się w „C”

  3. Zmienna globalna: zakres tego dotyczy całej aplikacji. Jest dostępny z każdego miejsca aplikacji. Zmienne globalne znajdują się również w segmencie DANYCH, ponieważ można uzyskać do nich dostęp w każdym miejscu aplikacji, a tym samym powiązanie zewnętrzne

Domyślnie wszystkie funkcje są globalne. W przypadku, gdy chcesz ukryć niektóre funkcje w pliku z zewnątrz, możesz poprzedzić funkcję statycznym słowem kluczowym. :-)


12
@Libin: Jak w przypadku 1) zmienne lokalne nie muszą znajdować się na stosie - zwykle znajdują się na stosie, ale mogą znajdować się w rejestrach, aw środowisku ARM częściej znajdują się w rejestrach niż na stosie (zależy od niektórych czynników - poziomu wywołania, liczby formalnych argumentów ..)
Artur,

4
@Libin: Co do 1) Jeśli uważasz, że „opróżnianie” jest nadpisywane - to źle. Wskaźnik stosu został właśnie przeniesiony w inne miejsce. Żadne „poprzednio ważne zmienne lokalne” nie są „opróżniane” / usuwane itp. Zmieszasz zakres zmienny z czasem przechowywania. Zakres informuje, skąd można uzyskać dostęp do var. Czas przechowywania informuje, jak długo istnieje. Możesz mieć zmienną lokalną ze statycznym czasem przechowywania. Oznacza to, że żyje „na zawsze”, ale można uzyskać do niego dostęp z funkcji, w której został zadeklarowany.
Artur,

2
Głosuj za niedokładnymi pojęciami i oczywistymi nieporozumieniami. Ściśle mówiąc, w C. nie ma zdefiniowanej „globalnej” ani „zmiennej” (jako rzeczownik). Prawdopodobnie możesz chcieć odnieść się do „obiektu zakresu pliku” zamiast do „zmiennej globalnej”, ale mówiąc o „zakresie” (w C it jest własnością identyfikatora ), to jest nonsens. (Oba terminy są zdefiniowane w C ++ normatywnie z nieco innymi znaczeniami.)
FrankHB

@Artur Myślę, że zapomniałeś „ tylko ” w „ To znaczy, że żyje„ na zawsze ”, ale można uzyskać dostęp (tylko) z funkcji, w której jest zadeklarowany. ” - To ważny szczegół, dlatego chciałbym wskazać to jawnie.
RobertS obsługuje Monikę Cellio

14

Zanim zaczniesz mówić o tym pytaniu, lepiej dokładnie znać termin jednostka tłumacząca , program i kilka podstawowych pojęć C ++ (tak naprawdę łączenie jest ogólnie jednym z nich). Musisz także wiedzieć, jaki jest zakres .

Podkreślę kilka kluczowych punktów, zwłaszcza. brakujące w poprzednich odpowiedziach.

Powiązanie jest właściwością nazwy , która jest wprowadzana przez deklarację . Różne nazwy mogą oznaczać ten sam byt (zazwyczaj obiekt lub funkcję). Zatem mówienie o powiązaniu jednostki jest zwykle nonsensem, chyba że masz pewność, że do jednostki będzie odwoływała się tylko unikalna nazwa z niektórych konkretnych deklaracji (zazwyczaj jednak jedna deklaracja).

Zauważ, że obiekt jest bytem, ​​ale zmienna nim nie jest. Mówiąc o powiązaniu zmiennej, w rzeczywistości chodzi o nazwę oznaczonego bytu (wprowadzoną przez określoną deklarację). Połączenie nazwy znajduje się w jednym z trzech: brak połączenia, połączenie wewnętrzne lub połączenie zewnętrzne.

Różne jednostki tłumaczeniowe mogą współdzielić tę samą deklarację poprzez dołączenie nagłówka / pliku źródłowego (tak, to sformułowanie standardu). Możesz więc odwoływać się do tej samej nazwy w różnych jednostkach tłumaczeniowych. Jeśli zadeklarowana nazwa ma powiązanie zewnętrzne, tożsamość encji, do której odnosi się nazwa, jest również wspólna. Jeśli zadeklarowana nazwa ma powiązanie wewnętrzne, ta sama nazwa w różnych jednostkach tłumaczeniowych oznacza różne jednostki, ale można odwoływać się do jednostki w różnych zakresach tej samej jednostki tłumaczeniowej. Jeśli nazwa nie ma powiązania, po prostu nie można skierować obiektu z innych zakresów.

(Ups ... Odkryłem, że to, co wpisałem, było po prostu powtórzeniem standardowego sformułowania ...)

Istnieją również inne mylące punkty, które nie są uwzględnione w specyfikacji języka.

  1. Widoczność (nazwy). Jest to także właściwość o zadeklarowanej nazwie, ale o innym znaczeniu niż powiązanie .
  2. Widoczność (efektu ubocznego) . To nie jest związane z tym tematem.
  3. Widoczność (symbolu). Tego pojęcia mogą używać rzeczywiste implementacje . W takich implementacjach symbolem o szczególnej widoczności w kodzie obiektowym (binarnym) jest zwykle cel odwzorowany z definicji encji, której nazwy mają takie same określone powiązanie w kodzie źródłowym (C ++). Jednak zwykle nie jest to gwarantowane jeden do jednego. Na przykład symbol w dynamicznym obrazie bibliotecznym można określić tylko jako współużytkowany w tym obrazie wewnętrznie z kodu źródłowego (zwykle związany z niektórymi rozszerzeniami lub ) lub opcji kompilatora, a obraz nie jest całym programem lub plikiem obiektowym przetłumaczonym z jednostki tłumaczeniowej, dlatego żadna standardowa koncepcja nie może go dokładnie opisać. Ponieważ symbol nie jest terminem normatywnym w C ++, jest jedynie szczegółem implementacji, nawet jeśli powiązane rozszerzenia dialektów mogły być powszechnie przyjęte.__attribute____declspec
  4. Dostępność. W C ++ zwykle chodzi o właściwość członków klasy lub klas podstawowych , co znowu jest inną koncepcją niezwiązaną z tematem.
  5. Światowy. W C ++ „globalny” odnosi się do globalnej przestrzeni nazw lub zakresu globalnej przestrzeni nazw. Ten ostatni jest mniej więcej równoważny zakresowi plików w języku C. Zarówno w C, jak i C ++, powiązanie nie ma nic wspólnego z zakresem, chociaż zakres (podobnie jak łączenie) jest również ściśle związany z identyfikatorem (w C) lub nazwą (w C ++) wprowadzonym przez jakąś deklarację.

Zasada powiązania z zakresu nazw constzmiennej jest coś specjalnego (a szczególnie różni się od constobiektu zadeklarowanego w zakresie plików w języku C, który ma również koncepcję powiązania identyfikatorów). Ponieważ ODR jest wymuszony przez C ++, ważne jest, aby zachować nie więcej niż jedną definicję tej samej zmiennej lub funkcji występującą w całym programie, z wyjątkiem inlinefunkcji . Jeśli nie ma takiej specjalnej zasady const, najprostsza deklaracja constzmiennej z inicjalizatorami (np.= xxx ) W nagłówku lub pliku źródłowym (często „plik nagłówka”) zawarta w wielu jednostkach tłumaczeniowych (lub w tej samej jednostce więcej niż jeden raz, choć rzadko) w programie naruszy ODR, co sprawia, że ​​warto go używaćconst zmienna, ponieważ niemożliwe jest zastąpienie niektórych makr obiektowych.


3
Ta odpowiedź brzmi bardzo sprawnie i może być bardzo dokładna (nie mogę tego ocenić), ale najprawdopodobniej nie jest tak zrozumiała, jak tego pragnie wiele osób przeglądających to pytanie, zamiast bezpośrednio czytać specyfikację języka. Przynajmniej na moje potrzeby będę trzymać się przyjętej odpowiedzi, ale nadal dziękuję za mały wgląd w specyfikację języka. 👍🏻
wedi

8

Myślę, że wewnętrzne i zewnętrzne powiązania w C ++ dają jasne i zwięzłe wyjaśnienie:

Jednostka tłumacząca odnosi się do pliku implementacyjnego (.c / .cpp) i wszystkich zawartych w nim plików nagłówkowych (.h / .hpp). Jeśli obiekt lub funkcja w takiej jednostce tłumaczenia ma wewnętrzne połączenie, ten konkretny symbol jest widoczny tylko dla linkera w tej jednostce tłumaczenia. Jeśli obiekt lub funkcja ma powiązanie zewnętrzne, linker może to również zobaczyć podczas przetwarzania innych jednostek tłumaczeniowych. Statyczne słowo kluczowe użyte w globalnej przestrzeni nazw powoduje, że symbol ma wewnętrzne powiązanie. Słowo kluczowe extern powoduje, że symbol ma zewnętrzny link.

Kompilator domyślnie łączy takie symbole, że:

Niestałe zmienne globalne mają domyślnie zewnętrzne połączenie
Stałe zmienne globalne mają domyślnie wewnętrzne połączenie
Funkcje mają domyślnie zewnętrzne połączenie


6

Połączenie określa, czy identyfikatory o identycznych nazwach odnoszą się do tego samego obiektu, funkcji lub innej jednostki, nawet jeśli identyfikatory te występują w różnych jednostkach tłumaczeniowych. Połączenie identyfikatora zależy od tego, jak zostało zadeklarowane. Istnieją trzy rodzaje powiązań:

  1. Powiązanie wewnętrzne : identyfikatory można zobaczyć tylko w jednostce tłumaczeniowej.
  2. Zewnętrzne powiązanie : identyfikatory można zobaczyć (i odnieść się do nich) w innych jednostkach tłumaczeniowych.
  3. Brak powiązania : identyfikatory można zobaczyć tylko w zakresie, w którym zostały zdefiniowane. Powiązanie nie wpływa na zakres

Tylko C ++ : możesz również mieć powiązanie między fragmentami kodu C ++ i innych niż C ++, co nazywa się łączeniem języka .

Źródło: IBM Program Linkage


5

Gruntownie

  • extern linkage zmienna jest widoczna we wszystkich plikach
  • internal linkage zmienna jest widoczna w jednym pliku.

Wyjaśnij: stałe zmienne domyślnie łączą się wewnętrznie, chyba że zadeklarowano inaczej jako zewnętrzne

  1. domyślnie zmienna globalna to external linkage
  2. ale constzmienna globalna tointernal linkage
  3. dodatkowa extern constzmienna globalna toexternal linkage

Całkiem niezły materiał o linkowaniu w C ++

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


1

W C ++

Każda zmienna o zasięgu pliku, która nie jest zagnieżdżona w klasie lub funkcji, jest widoczna we wszystkich jednostkach tłumaczeniowych w programie. Nazywa się to łączem zewnętrznym, ponieważ w czasie łączenia nazwa jest widoczna dla łącznika wszędzie, poza jednostką tłumaczeniową.

Zmienne globalne i funkcje zwykłe mają powiązanie zewnętrzne.

Nazwa obiektu statycznego lub funkcji w zakresie pliku jest lokalna dla jednostki tłumaczącej. To się nazywa jako wewnętrzne połączenie

Powiązanie odnosi się tylko do elementów, które mają adresy w czasie łączenia / ładowania; dlatego deklaracje klas i zmienne lokalne nie są powiązane.


const globalne zmienne mają wewnętrzne powiązanie.
Blood-HaZaRd
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.