Страницы

Поиск по вопросам

среда, 1 января 2020 г.

Как устроены указатели на уровне ассемблера?

#cpp #ассемблер #указатели


void foo(int a)
{
 a = a + a;
}

void foo(int *a)
{
 *a = *a + *a;
}


ASM:

foo(int):
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], edi
        sal     DWORD PTR [rbp-4]
        nop
        pop     rbp
        ret
foo(int*):
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        mov     rax, QWORD PTR [rbp-8]
        mov     eax, DWORD PTR [rax]
        lea     edx, [rax+rax]
        mov     rax, QWORD PTR [rbp-8]
        mov     DWORD PTR [rax], edx
        nop
        pop     rbp
        ret


Как устроены указатели на уровне ассемблера ?
    


Ответы

Ответ 1



У процессора, т.е. в языке ассемблера, фактически все операции в оперативной памяти происходят "по указателю". Обратиться к ячейке памяти (переменной) можно только по ее конкретному адресу. Адрес при этом должен быть доступен инструкции непосредственно, т.е. находиться в самой инструкции, в регистре или вычисляться из регистров и констант. Указатель в языке C это переменная в памяти, содержащая тот самый адрес, по которому надо обращаться к данным. В приведенных вами примерах параметр функции передается через регистр. В первом случае передано само 32 битное значение, поэтому оно в регистре edi. Во втором - это 64 битный адрес, т.е. указатель (он в 64 битном регистре rdi). В этих примерах так же много лишнего кода (видимо выключена оптимизация). В первом примере значение из регистра переноситься в локальную область стека (в переменную a) и она умножается на 2. Достаточно было бы sal edi. Хотя конечно одного ret было бы более чем достаточно, потому что код фактически ничего не делает, он меняет свою локальную переменную, которую никуда не возвращает. Второй пример в идеале можно сократить до: foo(int*): ; В rdi указатель на 32 битную переменную. mov eax, DWORD PTR [rdi] ; Помещаем в eax значение, находящееся по адресу (указателю) rdi sal eax ; сдвиг eax влево на 1 бит ( eax*=2 ) mov DWORD PTR [rdi], eax ; Помещаем eax обратно по адресу из rdi ret Обратите внимание, что работа с локальными переменными, находящимися в стеке шла точно так же, в rbp находится указатель на текущий кадр стека, доступ к переменным осуществляется по этому адресу с некоторым смещением ([rbp+8]). Фактически rbp это точно такой же указатель, по которому происходит работа. Но язык высокого уровня, для удобства программиста отличает просто переменные и указатели

Комментариев нет:

Отправить комментарий