В C ++ с передачей по значению вызывающая сторона создает копию, которую использует вызываемый. В x64 ABI некоторые аргументы передаются в регистрах. Регистры не имеют адресов.
Итак, предположим, у меня есть следующий класс:
class Self_Pointer
{
Self_Pointer* self;
Self_Pointer(const Self_Pointer& obj) : self(this) {}
}
При передаче в качестве аргумента он принимает указатель на себя. И теперь у меня есть эта функция:
f(Self_Pointer x) {}
f()
принимает значение Self_Pointer, переданное в регистр в соответствии с соглашением о вызовах. Вызывающая сторона f()
пришлось бы создать копию в реестре. Таким образом, вызывающая сторона запускает конструктор для Self_Pointer, расположенного в регистре. Однако регистры не имеют адресов, поэтому было бы невозможно запустить конструктор. Как решить эту дилемму?
Во-первых, копия Self_Pointer
не указывает на себя. Он указывает на оригинал, потому что сгенерированный компилятором конструктор копирования копирует значение self
от оригинала до копии.
Итак, когда вы передаете объект с копией f
, по факту x
не указывает на себя.
Если вы исправили это, написав подходящий конструктор копирования для Self_Pointer
затем потребуется реализация для обеспечения того, чтобы x
имеет адрес. Если это означает, что он не передается в регистрах, то он не будет передаваться в регистрах. Как правило, ABI не передают структуры в регистрах, если они не имеют агрегатного типа, как в вашем примере, поскольку он имеет определяемый пользователем конструктор.
Для другого примера рассмотрим:
void foo(int i) {
std::cout << &i << '\n';
}
Опять же, реализация обязана организовать это i
имеет адрес. Если он передается в регистр (и, как у регистров x64, нет адресов на этом конкретном оборудовании), то функция foo
должен пролиться i
в стек. Тогда у него будет адрес, как и у любой другой переменной стека.
Использование регистров (или, в данном случае, стека) для передачи параметров является деталью реализации конкретного компилятора. Чтобы получить адрес для передачи в функцию, компилятор копирует значение регистра во временное место в памяти (обычно в стеке), передает свой адрес туда, куда он должен идти, а затем загружает данные из временное местоположение обратно в тот же регистр, если это необходимо.
Нет, ты не можешь. Получаемый вами адрес всегда является виртуальным адресом ОЗУ. Вы не можете получить доступ к адресу реестра непосредственно в C ++.