В чем смысл внутренней компоновки в C ++

Я понимаю, что существует три возможных значения связи для переменной в C ++ - без привязки, внутренней связи и внешней связи.

Таким образом, внешняя связь означает, что идентификатор переменной доступен в нескольких файлах, а внутренняя связь означает, что она доступна в одном файле. Но в чем смысл внутренней увязки? Почему бы просто не иметь две возможные связи для идентификатора - без связи и внешней связи? Мне кажется, что глобальная (или файловая) область видимости и внутренняя связь служат одной и той же цели.

Есть ли какой-либо вариант использования, когда действительно полезна внутренняя связь, не охватываемая глобальной областью?

В приведенном ниже примере у меня есть два фрагмента кода: первый ссылается на статический int i11 (который имеет внутреннюю привязку), а второй - нет. Оба в значительной степени делают одно и то же, поскольку main уже имеет доступ к переменной i11 из-за области ее файла. Итак, зачем нужна отдельная связь, называемая внутренней связью.

static int i11 = 10;

int main()
{
extern int i11;
cout << ::i11;
return 0;
}

дает тот же результат, что и

static int i11 = 10;

int main()
{
 cout << ::i11;
 return 0;
}

РЕДАКТИРОВАТЬ: Просто для большей ясности, согласно приведенному ниже определению HolyBlackCat, внутренняя связь действительно означает, что вы можете пересылать объявление переменной в той же единице перевода. Но зачем вам вообще нужно делать это для переменной, которая уже доступна глобально в файле .. Есть ли какой-либо вариант использования этой функции?


person programmerravi    schedule 11.01.2018    source источник
comment
Если бы у переменной не было связи, вы бы вообще не смогли получить к ней доступ.   -  person    schedule 12.01.2018
comment
Внешняя связь предназначена для случаев, когда у вас есть несколько файлов .c (или .cpp) (не включенные файлы, они считаются частью файла .c, который их включает), которые вы хотите скомпилировать отдельно друг от друга.   -  person Cpp plus 1    schedule 12.01.2018
comment
Это. Я могу ошибаться, но «отсутствие связи», похоже, означает «не может быть объявлено вперед».   -  person HolyBlackCat    schedule 12.01.2018
comment
Как вы думаете, почему второй фрагмент демонстрирует другую связь?   -  person HolyBlackCat    schedule 12.01.2018
comment
HolyBlackCat - да, «нет связи», кажется, означает «не может быть объявлено вперед» ... а внутренняя связь, кажется, означает, что «может быть объявлено вперед в блоке перевода» - мой вопрос в том, зачем нам вообще нужно пересылать объявление переменной внутри единицы перевода, где она уже доступна глобально?   -  person programmerravi    schedule 12.01.2018
comment
Это может быть полезно для кольца статических функций, вызывающих друг друга, и, вероятно, разрешено для переменных только потому, что нет причин запрещать это.   -  person HolyBlackCat    schedule 12.01.2018
comment
HolyBlackCat - я утверждал, что второй фрагмент просто не использует функцию внутренней связи i11, в то время как первый фрагмент использует.   -  person programmerravi    schedule 12.01.2018
comment
Никакая связь не является в основном уникальной для локальных переменных (включая статические локальные переменные). Если вы хотите иметь глобальную переменную для единицы перевода, но не для программы, вам потребуется внутренняя связь. Я не совсем уверен, в чем путаница?   -  person Nir Friedman    schedule 12.01.2018


Ответы (2)


Примеры каждого:

Внешняя ссылка:

foo.h

    extern int foo; // Declaration

foo.cpp

    extern int foo = 42; // Definition

bar.cpp

    #include "foo.h"

    int bar() { return foo; } // Use

Внутренняя связь:

foo.cpp

    static int foo = 42; // No relation to foo in bar.cpp


bar.cpp

    static int foo = -43; // No relation to foo in foo.cpp

Нет связи:

foo.cpp

    int foo1() { static int foo = 42; foo++; return foo; }
    int foo2() { static int foo = -43; foo++; return foo; }

Конечно, вы согласитесь, что foo переменные в функциях foo1 и foo2 должны иметь память. Это означает, что у них, вероятно, должны быть имена из-за того, как работают ассемблеры и компоновщики. Эти имена не могут конфликтовать и не должны быть доступны для любого другого кода. Стандарт C ++ кодирует это как «без привязки». Есть несколько других случаев, когда он также используется, но для вещей, где немного менее очевидно, для чего используется хранилище. (Например, для class вы можете представить, что vtable имеет хранилище, но для typedef это в основном вопрос тонкостей языковой спецификации, касающейся области доступа имени.)

C ++ определяет модель связывания с наименьшим общим знаменателем, которую можно сопоставить с более богатыми моделями реальных компоновщиков на реальных платформах. На практике это очень несовершенно, и многие реальные системы в конечном итоге используют атрибуты, прагмы или флаги компилятора, чтобы получить больший контроль над типом связи. Чтобы сделать это и по-прежнему предоставить достаточно полезный язык, нужно использовать искажение имен и другие методы компиляции. Если бы C ++ когда-либо попытался обеспечить большую степень взаимодействия скомпилированного кода, как это делают виртуальные машины Java или .NET, весьма вероятно, что язык получит более ясный и более сложный контроль над связью.

РЕДАКТИРОВАТЬ: Чтобы более четко ответить на вопрос ... Стандарт должен определить, как это работает как для доступа к идентификаторам на исходном языке, так и для связывания скомпилированного кода. Определение должно быть достаточно строгим, чтобы правильно написанный код никогда не приводил к ошибкам для неопределенных или многократно определенных вещей. Конечно, есть способы сделать это лучше, чем в C ++, но это в значительной степени развитый язык, и на спецификацию в некоторой степени влияет субстрат, на котором он компилируется. Фактически существует три различных типа связи:

  • Внешняя связь: вся программа согласна с этим именем, и к нему можно получить доступ везде, где есть видимое объявление.
  • Внутренняя связь: это имя согласуется с одним файлом, и к нему можно получить доступ в любой области видимости объявления.
  • Нет связи: имя предназначено только для одной области и может быть доступно только в этой области.

В сборке они обычно отображаются в глобальное объявление, локальное объявление файла и локальное объявление файла с синтезированным уникальным именем.

Это также актуально для случаев, когда одно и то же имя объявляется с разной связью в разных частях программы и при определении того, что extern int foo относится к данному месту.

person Zalman Stern    schedule 12.01.2018

Внешняя связь предназначена для случаев, когда файлы компилируются независимо друг от друга (файлы #included .h, .c и .cpp не компилируются независимо друг от друга). Особенность внешней переменной заключается в том, что ее можно использовать между файлами, компилируемыми отдельно.

person Cpp plus 1    schedule 11.01.2018
comment
ОП уже сказал, что во втором абзаце. Настоящий вопрос заключается в том, что «нет связывания против статического связывания». - person HolyBlackCat; 12.01.2018
comment
@HolyBlackCat OP сказал несколько файлов, под которыми он, похоже, имел в виду включенные файлы. Не думаю, что он задавал бы вопрос, если бы знал, что внешние переменные можно использовать в отдельных единицах компиляции. - person Cpp plus 1; 12.01.2018
comment
Тем не менее, у поста есть и другие вопросы. Может это должен быть комментарий? .. - person HolyBlackCat; 12.01.2018