1. Принцип
Когда вы пишете:
int A = myfunction(12);
Это переведено на:
int A = @call(myfunction, 12);
, где @call
можно рассматривать как поиск по словарю. И если вы подумаете об аналогии со словарем, вы наверняка сможете узнать о слове ( смог ?), Прежде чем узнаете его определение. Все, что вам нужно, это чтобы во время выполнения определение находилось в словаре.
2. Точка на ABI
Как это @ call работает? Из-за ABI. ABI - это способ, который описывает многие вещи, и среди них, как выполнить вызов данной функции (в зависимости от ее параметров). Контракт на вызов прост: он просто говорит, где можно найти каждый из аргументов функции (некоторые будут в регистрах процессора, другие - в стеке).
Следовательно, @call на самом деле:
@push 12, reg0
@invoke myfunction
И определение функции знает, что ее первый аргумент ( x ) находится в reg0
.
3. Но я хоть словари были для динамических языков?
И вы правы, в некоторой степени. Динамические языки обычно реализуются с помощью хеш-таблицы для поиска символов, которая заполняется динамически.
Для C ++ компилятор преобразует модуль перевода (грубо говоря, предварительно обработанный исходный файл) в объект (.o
или .obj
в целом). Каждый объект содержит таблицу символов, на которые он ссылается, но определение которых неизвестно:
.undefined
[0]: myfunction
Тогда компоновщик соберет объекты и примирит символы. На данный момент существует два вида символов:
- те, которые находятся в библиотеке, и на которые можно ссылаться через смещение (конечный адрес все еще неизвестен)
- те, которые находятся за пределами библиотеки и чей адрес полностью неизвестен до времени выполнения.
Оба могут рассматриваться одинаково.
.dynamic
[0]: myfunction at <undefined-address>
И тогда код будет ссылаться на запись поиска:
@invoke .dynamic[0]
Когда библиотека загружена (например, DLL_Open
), среда выполнения наконец узнает , где символ отображается в памяти, и перезаписывает <undefined-address>
реальным адресом (для этого прогона) .