Как правило, данные, котоорыми оперирует программа, размещаются в памяти. Для симуляции памяти в Python можно использовать список. Например:
mem = [0,0,0,0,0,0,0,0] # или mem = [0] * 8
Переменная mem представляет условно память из 8 ячеек, каждая из которых хранит значение 0.
Для адресации к отдельным ячейкам памяти обычно применяются адреса в шестнадцатеричном формате. Например, в данном случае мы можем представить память следующим образом:
0x0000 | 0x0001 | 0x0002 | 0x0003 | 0x0004 | 0x0005 | 0x0006 | 0x0007 |
mem[0] | mem[1] | mem[2] | mem[3] | mem[4] | mem[5] | mem[6] | mem[7] |
Работая с отдельными элементами этого списка, мы фактически можем симулировать работу с соответствующими ячейками памяти. Например:
mem = [0]*8 # Память из 8 ячеек mem[0] = 4 # По адресу 0x0000 сохраняем число 4 mem[1] = 5 # По адресу 0x0001 сохраняем число 5 sum = mem[0] + mem[1] # Складываем значения ячеек 0x0000 и 0x0001 mem[2] = sum # Сохраняем сумму в ячейку 0x0002 print("mem[2] =", mem[2]) # Выводим на консоль значение из ячейки 0x0002 print("Memory =", mem) # печать всей памяти
Консольный вывод:
mem[2] = 9 Memory = [4, 5, 9, 0, 0, 0, 0, 0]
Косвенная адресация представляет ситуацию, когда одна ячейка памяти хранит адрес другой ячейки памяти. И таким образом, через одну ячейку памяти можно обратиться к другой. Например:
mem = [5,6,7,0,0,3,5,0] # Условная память val0 = mem[mem[0]] # val0 = 3 val1 = mem[mem[1]] # val1 = 5 sum = val0 + val1 # sum = 8 mem[mem[2]] = sum # Сохраняем результат в ячейку по адресу mem[2] print("Memory =", mem) # Memory = [5, 6, 7, 0, 0, 3, 5, 8]
Здесь первые три элемента в списке mem представляют индексы других элементов из списка mem (то есть условно адреса других ячеек в памяти). Например, возьмем следующее выражение
val0 = mem[mem[0]]
Здесь сначала получаем значение из mem[0] - индекс или адрес некоторого другого значения. Затем по этому индексу/адресу берем значение и помещаем в переменную val0. Таким образом, процесс разбивается на две части:
mem[0] -> 5 mem[5] -> 3
Собственно поэтому адресация называется косвенной - так как надо сначала получить адрес ячейки памяти, а лишь потом можно получить собственно значение по этому адресу.
Аналогичным образом получаем значение в переменную val1, складываем обе переменных и также с помощью косвенной адресации сохраняем результат в ячайку памяти, чей индекс хранится по индексу 2:
mem[mem[2]] = sum
Опять же получается двухэтапный прочесс:
mem[2] -> 7 sum -> mem[7]
При работе с памятью могут применяться указатели, которые упрощают косвенную адресацию. Указатели - это значения, которые хранят адреса ячеек памяти:
mem = [5,6,7,0,0,3,5,0] # Условная память pointer0 = mem[0] # Указатель pointer0 указывает на ячейку памяти с адресом 0x0005 pointer1 = mem[1] # Указатель pointer1 указывает на ячейку памяти с адресом 0x0006 pointer2 = mem[2] # Указатель pointer2 указывает на ячейку памяти с адресом 0x0007 val0 = mem[pointer0] # Получаем значение по указателю pointer0 val1 = mem[pointer1] # Получаем значение по указателю pointer1 sum = val0 + val1 # Выполняем сложение mem[pointer2] = sum # Сохраняем результат в ячейку по указателю pointer2 print("Memory =", mem) # Memory = [5, 6, 7, 0, 0, 3, 5, 8]
Здесь переменная pointer0 получает значение из mem[0] (ячейку памяти с условным адресом 0x0000). В этой ячейке хранится число 5. Но это не просто число. Это значение мы будем интерпретировать как адрес другой ячейки (фактически mem[5]). Аналогично переменная pointer1 получает в качестве значения число mem[1] - число 6, то есть ячейку с условным адресом 0x0006 (фактически mem[6]). То есть по факту указатели выступают в качестве индексов относительно начала памяти.
Далее, применяя указатель, мы можем получить по нему значение:
val0 = mem[pointer0] # Получаем значение по указателю pointer0; val0 = 3 val1 = mem[pointer1] # Получаем значение по указателю pointer1; val1 = 5
Выполнив сложение, сохраняем сумму по адресу на которую указывает указатель pointer2:
sum = val0 + val1 # Выполняем сложение mem[pointer2] = sum # Сохраняем результат в ячейку по указателю pointer2