Vektoren: Indizierung nach Position

Vektoren: Indizierung nach Position

Ein Vektor ist ein Array: eine Folge von Zahlen, die im Speicher nebeneinander abgelegt sind. In der vorherigen Lektion hast du einen summiert, indem du einen Zeiger für jedes Element um 4 Bytes vorwärts bewegt hast. Dieselbe Methode funktioniert auch hier. Es gibt eine weitere gängige Möglichkeit, auf ein Element zuzugreifen: über den Index. Ein Index ist die Position eines Elements vom Anfang an gezählt, wobei das erste Element den Index 0 hat, das zweite den Index 1, und so weiter.

Einen Vektor durch einen wandernden Zeiger summieren

Summiere einen Vektor, indem du einen Zeiger voranleitest
.data
a: .word 5, 10, 15, 20 # vier Elemente
.text
la t0, a # t0 = Adresse von a
li t1, 4 # t1 = Anzahl der verbleibenden Elemente
li t2, 0 # t2 = laufende Summe
loop:
beq t1, zero, done
lw t3, 0(t0) # lädt das aktuelle Element
add t2, t2, t3 # addiert zur Summe
addi t0, t0, 4 # zum nächsten Wort weiter (4 Bytes)
addi t1, t1, -1 # eines weniger
j loop
done:
mv a0, t2 # gibt die Summe aus: 5+10+15+20 = 50
li a7, 1
ecall
li a7, 10
ecall

Die Schleife läuft wie folgt ab. t0 enthält die Adresse von a, und t1 enthält die Anzahl der verbleibenden Elemente, beginnend bei 4. Bei jedem Durchlauf beendet die Schleife, wenn der Zähler null erreicht. lädt das Element an der Adresse in t0. addiert diesen Wert zur laufenden Summe. rückt den Zeiger zum nächsten Wort vor. verringert den Zähler. Nach vier Durchläufen (5 + 10 + 15 + 20) gibt done 50 aus. Dies ist dasselbe Muster wie in der Speicher-Lektion, nun auf einen Vektor angewendet.

Vom Index zur Adresse

Die Indexierung berechnet eine Adresse aus einem Basiszeiger und einer Position: address = base + index * 4. Die 4 ist die Größe eines Wortes in Bytes. Ein Compiler erzeugt dieselbe Berechnung für a[i] in einer Sprache wie C. Der Index selbst greift nie auf den Speicher zu. Nur die daraus berechnete Adresse tut dies.

Index zu Adresse, unter Verwendung einer Verschiebung
slli t5, t3, 2 # t5 = Index * 4 (Wortgröße)
add t6, t0, t5 # t6 = Adresse von element[index]
lw t6, 0(t6) # t6 = element[index]

verschiebt den Index in t3 um 2 Bits nach links, was ihn mit 4 multipliziert (zwei Verdopplungen: 2 x 2), der Größe eines Wortes. Addiert man das Ergebnis zum Basiszeiger, erhält man die Adresse von element[index]. Diese Verschiebung ist dieselbe Zweierpotenz-Methode aus der Arithmetik-Lektion, und sie ist die Standardmethode, um einen Index mit der Größe eines Wortes zu skalieren.

Adressberechnung
address=base+0×4=base+0
a[0]3
0x10010000
a[1]6
0x10010004
a[2]9
0x10010008
a[3]12
0x1001000c

a[0]: Index 0, Offset 0.

Der Zeigerbalken und das Hex-Badge bewegen sich gemeinsam, während der Index 0, 1, 2, 3 durchläuft. Sie stellen dieselbe Tatsache in zwei Formen dar: i als Position und die Adresse, die diese Position erzeugt, nachdem sie mit 4 skaliert und zur Basis addiert hat. Die Hex-Zahlen sind die tatsächlichen Adressen, die a belegt, wenn der Simulator dieses Programm ausführt. Das Ziel ist, die nächste Adresse vorherzusagen, bevor sie erscheint. Dadurch kannst du innerhalb einer inkrementierenden Schleife oder a[i] in einer C-ähnlichen Sprache lesen und dir die Speicheradresse vorstellen, auf die zugegriffen wird, anstatt die Notation als Blackbox zu behandeln.

Einen Vektor per Index summieren

Summiere einen Vektor unter Verwendung eines Index
.data
a: .word 5, 10, 15, 20 # vier Elemente
.text
la t0, a # t0 = Basisadresse von a
li t1, 4 # t1 = Anzahl der Elemente
li t2, 0 # t2 = Index, beginnt bei 0
li t3, 0 # t3 = laufende Summe
loop:
beq t2, t1, done
slli t4, t2, 2 # t4 = Index * 4 (Byte-Offset)
add t5, t0, t4 # t5 = Adresse von a[index]
lw t5, 0(t5) # t5 = a[index]
add t3, t3, t5 # total += a[index]
addi t2, t2, 1 # zum nächsten Index weiter
j loop
done:
mv a0, t3 # gibt die Summe aus: 5+10+15+20 = 50
li a7, 1
ecall
li a7, 10
ecall

Die Schleife läuft wie folgt ab. t2 ist der Index, beginnend bei 0. Bei jedem Durchlauf beendet die Schleife, wenn der Index 4 erreicht, die in t1 gehaltene Elementanzahl. wandelt den Index in einen Byte-Offset um. addiert diesen Offset zur Basisadresse in t0, um die Adresse von a[index] zu erzeugen. lädt den dort gespeicherten Wert. Dieser Wert wird zur laufenden Summe in t3 addiert, und rückt zum nächsten Index vor. Nach vier Durchläufen (5 + 10 + 15 + 20) gibt done 50 aus.

Addition zweier Vektoren, Element für Element

c[i] = a[i] + b[i]
.data
a: .word 3, 6, 9, 12 # erster Vektor
b: .word 1, 2, 3, 4 # zweiter Vektor
c: .space 16 # Platz für 4 Ergebniswörter
.text
la t0, a # Basisadresse von a
la t1, b # Basisadresse von b
la t2, c # Basisadresse von c
li t3, 0 # Index, beginnt bei 0
li t4, 4 # Anzahl der Elemente
li s0, 0 # laufende Summe
loop:
beq t3, t4, done
slli t5, t3, 2 # Byte-Offset = Index * 4
add t6, t0, t5 # Adresse von a[index]
lw t6, 0(t6) # Wert von a[index]
add a0, t1, t5 # Adresse von b[index]
lw a0, 0(a0) # Wert von b[index]
add t6, t6, a0 # a[index] + b[index]
add a0, t2, t5 # Adresse von c[index]
sw t6, 0(a0) # c[index] = a[index] + b[index]
add s0, s0, t6 # Summe += c[index]
addi t3, t3, 1 # zum nächsten Index weiter
j loop
done:
mv a0, s0 # gibt die Summe aus: 4+8+12+16 = 40
li a7, 1
ecall
li a7, 10
ecall

Die Schleife läuft wie folgt ab. wandelt den Index t3 in einen Byte-Offset um. Addiert man ihn zu jedem Basiszeiger (t0, t1, t2), erhält man die Adressen von a[i], b[i] und c[i]. Bei jedem Durchlauf werden beide Elemente geladen, addiert, in c gespeichert und zur laufenden Summe in s0 addiert. rückt zum nächsten Index vor, und die Schleife wiederholt sich, bis alle vier Elemente verarbeitet sind.

💡
Dieses Indexmuster, base + index * Elementgröße, ist die Art und Weise, wie jeder Array-Zugriff kompiliert wird, in jeder Sprache. Behalte es im Hinterkopf: Die nächste Lektion verwendet dieselbe Idee zweimal gleichzeitig, um auf eine Matrix zuzugreifen.