Вектори: індексація за позицією

Вектори: індексація за позицією

Вектор — це масив: послідовність чисел, що зберігаються поруч одне з одним у пам'яті. У попередньому уроці ви підсумовували його, переміщуючи вказівник уперед на 4 байти для кожного елемента. Той самий метод працює й тут. Є ще один поширений спосіб дістатися до елемента: за індексом. Індекс — це позиція елемента, що відраховується від початку, де перший елемент має індекс 0, другий має індекс 1, і так далі.

Підсумовування вектора просуванням вказівника

Сумувати вектор, просуваючи вказівник
.data
a: .word 5, 10, 15, 20 # чотири елементи
.text
la t0, a # t0 = адреса a
li t1, 4 # t1 = кількість елементів, що залишилися
li t2, 0 # t2 = поточна сума
loop:
beq t1, zero, done
lw t3, 0(t0) # завантажити поточний елемент
add t2, t2, t3 # додати до суми
addi t0, t0, 4 # перейти до наступного слова (4 байти)
addi t1, t1, -1 # на один менше залишилося
j loop
done:
mv a0, t2 # вивести суму: 5+10+15+20 = 50
li a7, 1
ecall
li a7, 10
ecall

Цикл виконується так. t0 зберігає адресу a, а t1 зберігає кількість елементів, що залишилися, починаючи з 4. На кожному проході завершує цикл, коли лічильник досягає нуля. завантажує елемент за адресою в t0. додає це значення до поточної суми. просуває вказівник до наступного слова. зменшує лічильник. Після чотирьох проходів (5 + 10 + 15 + 20) done виводить 50. Це той самий шаблон, використаний в уроці про пам'ять, тепер застосований до вектора.

Від індексу до адреси

Індексація обчислює адресу з базового вказівника та позиції: address = base + index * 4. Число 4 — це розмір слова в байтах. Компілятор генерує таке саме обчислення для a[i] у такій мові, як C. Сам індекс ніколи не звертається до пам'яті. Це робить лише обчислена з нього адреса.

Індекс в адресу, використовуючи зсув
slli t5, t3, 2 # t5 = індекс * 4 (розмір слова)
add t6, t0, t5 # t6 = адреса element[index]
lw t6, 0(t6) # t6 = element[index]

зсуває індекс у t3 вліво на 2 біти, що множить його на 4 (два подвоєння: 2 x 2) — розмір одного слова. Додавання результату до базового вказівника дає адресу element[index]. Цей зсув — той самий метод степенів двійки з уроку арифметики, і це стандартний спосіб масштабування індексу на розмір слова.

Обчислення адреси
адреса=база+0×4=база+0
a[0]3
0x10010000
a[1]6
0x10010004
a[2]9
0x10010008
a[3]12
0x1001000c

a[0]: індекс 0, зсув 0.

Смуга вказівника та шістнадцятковий значок рухаються разом, поки індекс проходить через 0, 1, 2, 3. Вони подають той самий факт у двох формах: i як позиція та адреса, яку ця позиція породжує після того, як масштабує її на 4 і додає до бази. Шістнадцяткові числа — це фактичні адреси, які займає a, коли симулятор запускає цю програму. Мета — передбачити наступну адресу до того, як вона з'явиться. Це дозволяє читати всередині інкрементувального циклу або a[i] у C-подібній мові та уявляти адресу пам'яті, до якої він звертається, замість того щоб сприймати запис як чорну скриньку.

Підсумовування вектора за індексом

Сумувати один вектор за допомогою індексу
.data
a: .word 5, 10, 15, 20 # чотири елементи
.text
la t0, a # t0 = базова адреса a
li t1, 4 # t1 = кількість елементів
li t2, 0 # t2 = індекс, починається з 0
li t3, 0 # t3 = накопичувальна сума
loop:
beq t2, t1, done
slli t4, t2, 2 # t4 = індекс * 4 (зміщення в байтах)
add t5, t0, t4 # t5 = адреса a[index]
lw t5, 0(t5) # t5 = a[index]
add t3, t3, t5 # total += a[index]
addi t2, t2, 1 # перейти до наступного індексу
j loop
done:
mv a0, t3 # вивести total: 5+10+15+20 = 50
li a7, 1
ecall
li a7, 10
ecall

Цикл виконується так. t2 — це індекс, починаючи з 0. На кожному проході завершує цикл, коли індекс досягає 4 — кількості елементів, що зберігається в t1. перетворює індекс на байтове зміщення. додає це зміщення до базової адреси в t0, щоб отримати адресу a[index]. завантажує значення, що там зберігається. Це значення додається до поточної суми в t3, а переходить до наступного індексу. Після чотирьох проходів (5 + 10 + 15 + 20) done виводить 50.

Додавання двох векторів, елемент за елементом

c[i] = a[i] + b[i]
.data
a: .word 3, 6, 9, 12 # перший вектор
b: .word 1, 2, 3, 4 # другий вектор
c: .space 16 # місце для 4 слів результату
.text
la t0, a # базова адреса a
la t1, b # базова адреса b
la t2, c # базова адреса c
li t3, 0 # індекс, починається з 0
li t4, 4 # кількість елементів
li s0, 0 # накопичувальна сума
loop:
beq t3, t4, done
slli t5, t3, 2 # зміщення в байтах = індекс * 4
add t6, t0, t5 # адреса a[index]
lw t6, 0(t6) # значення a[index]
add a0, t1, t5 # адреса b[index]
lw a0, 0(a0) # значення b[index]
add t6, t6, a0 # a[index] + b[index]
add a0, t2, t5 # адреса c[index]
sw t6, 0(a0) # c[index] = a[index] + b[index]
add s0, s0, t6 # total += c[index]
addi t3, t3, 1 # перейти до наступного індексу
j loop
done:
mv a0, s0 # вивести total: 4+8+12+16 = 40
li a7, 1
ecall
li a7, 10
ecall

Цикл виконується так. перетворює індекс t3 на байтове зміщення. Додавання його до кожного базового вказівника (t0, t1, t2) дає адреси a[i], b[i] і c[i]. На кожному проході обидва елементи завантажуються, додаються, зберігаються в c і додаються до поточної суми в s0. переходить до наступного індексу, і цикл повторюється, поки всі чотири елементи не буде оброблено.

💡
Цей шаблон індексації, база + індекс * розмір елемента, — це те, як компілюється будь-яке звернення до масиву, у будь-якій мові. Тримайте це на думці: наступний урок використовує ту саму ідею двічі одночасно, щоб дістатися до матриці.