mov var, 0x1234
выдает ошибку, потому что var
не является режимом адресации, [var]
будет, но, тем не менее, вы все равно получите ошибку, поскольку NASM не может определить размер операции.
mov DWORD [var], 0x1234
будет делать (при условии, что ваша модель памяти использует 32-битные близкие указатели).
x86 не имеет косвенных перемещений адресов, в то время как в C вы можете использовать указатели "непосредственно из памяти"(грубо говоря), в сборке вы должны сначала загрузить указатель в регистр, а затем использовать этот регистр в качестве адреса.
Смотрите сами на Godbolt .
int* bar;
int foo()
{
return *bar;
}
------
foo(): # @foo()
mov eax, dword ptr [bar]
mov eax, dword ptr [eax]
ret
bar:
.long 0
Операция deference требует дополнительной инструкции, это может «выглядеть» как использование двух ссылок, но это не так (на самом деле в NASM можно видеть имя переменной как указатель на неено давайте все упростим).
Вы можете обращаться с указателем как с любой другой переменной (включая копировать ее), просто копируя ее в регистр и передавая ее (первая mov
в приведенном выше примере делаетчто).
Например
mov eax, DWORD [var]
call foo ;Call foo with the pointer in EAX
mov DWORD [var], 0x1234 ;Change the pointer value
Относительно резервирования памяти : любая переменная, когда хранится в памяти, занимает некоторое место, вот что делает программу состоянием.
resb
используется для неинициализированных данных, здесь используются функции ELF для экономии места в скомпилированном двоичном файле на диске .
Если вы создаете свою собственную ОС, вы можетене использовать ELF вообще, поэтому resb
(и аналогичные) могут просто откатиться при выделении инициализированной нулем переменной (NASM предупредит об этом).
Вы можете использовать стек для временного хранения ваших переменных, если они имеют ограниченную область действия;это будет использовать то же пространство там, ограничивая объем памяти.
В качестве альтернативы вы можете использовать %define
для определения символов уровня сборки, они похожи (но не идентичны) на C #define
s.
Например:
%define MY_PTR_TO_SOMETHING 0x1234
mov DWORD [MY_PTR_TO_SOMETHING], 1 ;In C this is *MY_PTR_TO_SOMETHING = 1;
Это не выделит место для MY_PTR_TO_SOMETHING
, так как это просто псевдоним для номера 0x1234
.Вы можете думать об этом как C #define MY_PTR_TO_SOMETHING ((int*)0x1234)
.Или как static const int *MY_PTR_TO_SOMETHING = (int*)0x1234;
с компилятором, который оптимизирует фактическое статическое хранилище для самого объекта-указателя.
Обратите внимание, однако, что уровень косвенности пропал благодаря тому факту, что значение указателя теперь доступно какизвестная константа.
Конечно, вы все равно можете обойти это:
mov eax, MY_PTR_TO_SOMETHING ;EAX = Holds the value of MY_PTR_TO_SOMETHING
mov ebx, DWORD [eax] ;EBX = Load a DWORD from the address 0x1234
;Store or copy the EAX register to pass the pointer around
Работа с указателями может сбивать с толку, если вы не используете режимы адресации x86.Мой совет: прочитайте инструкцию по эксплуатации для mov
и убедитесь, что понимаете разницу между mov eax, 0x1234
и mov eax, DWORD [0x1234]
.
Если вам нужен указатель, который вы можете изменить во время выполнения, вам нужнонекоторая память для значения указателя;это не может быть константа времени сборки или адрес метки.
В C вы бы использовали int *ptr;
, который зарезервировал бы место для объекта размером с указатель либо в статическом хранилище (в глобальной области видимости)или в регистре (или пространстве стека) для локальной области видимости.