Re: Что такое "связывание переменных"?
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.11.21 09:32
Оценка: 2 (1) +2
Здравствуйте, Эйнсток Файр, Вы писали:

ЭФ>

питон широко использует динамическое связывание переменных, которое никуда не девается в откомпилированном коде

ЭФ>https://habr.com/ru/post/481782/

ЭФ>Правильно ли я понимаю, что тип переменной хранится в рантайме рядом с самой переменной, и используется для выбора одной из функций из нескольких одинаковых по назначению, отличающихся только типами (размерами) аргументов?

Нет. Связывание — это процесс, который связывает имя объекта с самим объектом.
Чаще всего этот термин употребляют в контексте поиска метода — к примеру, Function(x) может ссылаться (в зависимости от языка и контекста) на десяток разных методов с именем Function.
Если мы говорим про переменные, то каждая переменная — это, в конце концов, некая "ячейка памяти", которая однозначно определяется её адресом.
Соответственно, связывание переменных — это сопоставление имени некоторого адреса.
Практически в любом языке программирования одно и то же имя может сопоставляться с разными адресами, даже в один и тот же момент времени:
int main() {
    static int x = 5;
    std::cout << x << endl; // это - x №1
    {
        static int x = 6;
        std::cout << x << endl; // это - x №2
    }
    std::cout << x << endl; // а это - снова x №1
}


public int CountTreeLeaves<T>(T node)
  where T: IEnumerable<T>
{
   var c = 0;
   foreach(var child in node)
      c+= CountTreeLeaves(child); // когда мы обходим глубокое дерево, у нас одновременно существуют несколько экземпляров переменной с именем "c" - по одной на уровень вложенности вызова
   return c;
}


Но в нормальных, некурящих языках, это сопоставление делается статически — то есть при компиляции программы. В первом примере у нас получилось два разных x с двумя разными адресами. Но вопрос о том, куда обращаться при выводе x, решается один раз, при компиляции, а при исполнении уже нет никакой свободы выбора. Иногда, как в первом примере, связывание получается абсолютным — т.е. адрес ячейки однозначно известен; даже если мы вызовем main() рекурсивно, оба x будут ссылаться на те же адреса, что и во внешнем вызове. Иногда, как во втором примере (и вообще в большинстве случаев) адреса указываются относительно текущего фрейма стека (что и позволяет с иметь одновременно множество не мешающих друг другу значений).
Те места, где связывание делается динамически, в таких языках обозначаются специальным образом (и, как правило, это относится исключительно к связыванию методов, а не переменных).

А в языках типа JS и питона связывание переменных выполняется динамически. То есть адрес ячейки памяти, к которой нужно обратиться при вычислении выражения с участием переменной, определяется в момент исполнения программы; заранее этого сделать нельзя из-за правил связывания, описанных в языке.

ЭФ>Ещё мне непонятно, зачем нужно ключевое слово var в C#. Да, исторические причины, привычки, то-сё.

ЭФ>Но если проектировать синтаксис языка начисто, то:
ЭФ>во-первых, тип переменных заранее объявлять не нужно (он выводится);
ЭФ>во-вторых, тип переменной можно сделать зависимым от контекста,
ЭФ>для того чтобы можно было в двух последовательных строчках написать:
ЭФ>data=1; // целое
ЭФ>data="lala"; // строка
И какова будет семантика этого обращения?
Что должно быть выведено, если третьей строкой мы напишем System.Console.WriteLine(data)?

ЭФ>Язык при этом останется типизированным, потому что

ЭФ>компилятор будут генерировать разный код в зависимости от типа переменной в месте использования.
Это как раз понятно. Непонятно, как определить тип переменной "в месте использования".
ЭФ>Рантайм не будет хранить тип переменной, потому что об этом будет заботится компилятор.
Это и есть статическое связывание, которое уже и так используется в C#.
ЭФ>Что со мной не так?
Нехватка образования. Ничего страшного — это поправимо
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.