Что такое внешняя связь и внутренняя связь?


337

Я хочу понять внешнюю связь и внутреннюю связь и их различие.

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

constпеременные внутренне связаны по умолчанию, если иное не объявлено как extern.

Ответы:


279

Когда вы пишете файл реализации ( .cppи .cxxт. Д.), Ваш компилятор генерирует модуль перевода . Это исходный файл из вашей реализации плюс все заголовки, которые вы #includeв нем поместили.

Внутренняя связь относится ко всему только в рамках единицы перевода .

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


112
Я бы поддержал это, за исключением одного глюка: модуль перевода - это не «каким-то образом объектный файл», это исходный код, из которого компилятор создает объектный файл.
ВОО

4
@FrankHB, что за «что-то более важное» отсутствует в ответе?
математик

2
@ Mathematician Извините за опоздание ... Я думаю, что проблема должна быть очевидной (помимо точности формулировки). Этот ответ неполон, поскольку вопрос о правиле constпеременных (а также о его назначении) здесь полностью отсутствует.
FrankHB

294

Как сказал 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
Реализация ключевого слова "export" высветила разницу между функцией, объявленной как static, и функцией, объявленной в безымянном пространстве имен. Подводя итог, насколько я могу, шаблон функции, объявленный с ключевым словом экспорта в одной единице перевода, может ссылаться на функцию, определенную в безымянном пространстве имен другой единицы перевода в результате двухфазного поиска. ( ddj.com/showArticle.jhtml?articleID=184401584 )
Ричард Корден

Что если я сделаю следующее: 1.cpp <code> const int ci; </ code> 2.cpp <code> extern const int ci; </ code>
Раджендра Уппал

2
@Rajenda вы получите неразрешенную ошибку символа (извините за девятимесячную задержку в ответе, я пропустил этот комментарий).
Мотти

4
Информация, которая может значительно улучшить этот ответ: 1) static больше не считается устаревшим в C ++ 11. 2) анонимные члены пространства имен в C ++ 11 по умолчанию имеют внутреннюю связь. См stackoverflow.com/questions/10832940/...
KLAIM

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

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

Надеюсь, это поможет!


2
Важно отметить, что при использовании в определениях локальных переменных staticдопускает ленивую однократную инициализацию (что может быть полезно, если вам нужен объект global-ish, но нужно контролировать, когда он создается из-за проблем с глобальным порядком построения и не может динамически выделить его использование newболее подробных схем инициализации может выходить за рамки того, что необходимо для рассматриваемого объекта, что подразумевает, что в основном это проблема встроенных систем, использующих C ++).
JAB

1
Очень хороший экзамен, сделал мой день.
Blood-HaZaRd

28

В терминах «C» (потому что ключевое слово static имеет различное значение между «C» и «C ++»)

Давайте поговорим о различных сферах в «С»

СФЕРА ПРИМЕНЕНИЯ: Это в основном, как долго я могу что-то увидеть и как далеко.

  1. Локальная переменная: область видимости только внутри функции. Он находится в области памяти STACK. Это означает, что каждый раз, когда функция вызывается, все переменные, которые являются частью этой функции, включая аргументы функции, создаются заново и уничтожаются, когда элемент управления выходит из функции. (Потому что стек очищается каждый раз, когда функция возвращается)

  2. Статическая переменная: область действия для файла. Он доступен в любом месте файла,
    в котором он объявлен. Он находится в сегменте памяти DATA. Поскольку это может быть доступно только внутри файла и, следовательно, внутренняя связь. Любые
    другие файлы не могут видеть эту переменную. На самом деле ключевое слово STATIC - это единственный способ, которым мы можем ввести некоторый уровень данных или функции,
    скрывающейся в «C».

  3. Глобальная переменная: область действия для всего приложения. Это доступно из любой точки приложения. Глобальные переменные также находятся в сегменте DATA, так как к нему можно получить доступ из любого места приложения и, следовательно, ВНЕШНЕГО связывания

По умолчанию все функции являются глобальными. В случае, если вам нужно скрыть некоторые функции в файле извне, вы можете добавить к этой функции ключевое слово static. :-)


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

4
@Libin: Что касается 1) Если вы считаете «сброс» перезаписью - это неправильно. Указатель стека просто перемещен в другое место. Никакие «ранее действующие локальные переменные» не «очищаются» / очищаются и т. Д. Вы смешиваете переменную область действия с продолжительностью хранения. Scope сообщает, откуда вы можете получить доступ к var. Срок хранения говорит о том, как долго он существует. Вы можете иметь локальную переменную со статической продолжительностью хранения. Это означает, что он живет «вечно», но к нему можно получить доступ из функции, в которой он объявлен.
Артур

2
Даунвот за неточные понятия и очевидные заблуждения. Строго говоря, в C. не определено ни «глобальное», ни «переменное» (как существительное). Возможно, вы захотите ссылаться на «объект области файла», а не на «глобальную переменную», а говорить о «области» (в C это это свойство идентификатора ) это ерунда. (Оба термина определены в C ++ нормативно с немного различными значениями.)
FrankHB

@Artur Я думаю, что вы забыли « only » в « Это означает, что оно живет« навсегда », но к нему можно получить доступ (только) из функции, в которой оно объявлено ». - Это важная деталь, поэтому я хотел бы отметить это явно.
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), которые он включает. Если объект или функция внутри такой единицы перевода имеет внутреннюю связь, то этот конкретный символ виден только компоновщику внутри этой единицы перевода. Если объект или функция имеет внешнюю связь, компоновщик также может видеть ее при обработке других единиц перевода. Ключевое слово static при использовании в глобальном пространстве имен заставляет символ иметь внутреннюю связь. Ключевое слово extern приводит к тому, что символ имеет внешнюю связь.

Компилятор по умолчанию связывает символы так, что:

Неконстантные глобальные переменные по умолчанию имеют внешнюю связь
Константные глобальные переменные по умолчанию имеют внутреннюю связь
Функции по умолчанию имеют внешнюю связь


6

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

  1. Внутренняя связь : идентификаторы можно увидеть только внутри единицы перевода.
  2. Внешняя связь : идентификаторы можно увидеть (и сослаться) в других единицах перевода.
  3. Нет связи : идентификаторы можно увидеть только в той области, в которой они определены. Связь не влияет на объем

Только C ++ : вы также можете иметь связь между фрагментами кода C ++ и не-C ++, что называется языковой связью .

Источник: IBM Program Linkage


5

В принципе

  • extern linkage переменная видна во всех файлах
  • internal linkage переменная видна в одном файле.

Пояснение: переменные const внутренне связаны по умолчанию, если иное не объявлено как extern

  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 имеют внутреннюю связь.
Blood-HaZaRd
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.