Vetores: indexação por posição

Vetores: indexação por posição

Um vetor é um array: uma sequência de números armazenados um ao lado do outro na memória. Na lição anterior, você somou um avançando um ponteiro em 4 bytes para cada elemento. Esse mesmo método funciona aqui. Há outra maneira comum de alcançar um elemento: por índice. Um índice é a posição de um elemento contada a partir do início, em que o primeiro elemento tem índice 0, o segundo tem índice 1, e assim por diante.

Somar um vetor avançando um ponteiro

Some um vetor avançando um ponteiro
.data
a: .word 5, 10, 15, 20 # quatro elementos
.text
la t0, a # t0 = endereço de a
li t1, 4 # t1 = número de elementos restantes
li t2, 0 # t2 = soma corrente
loop:
beq t1, zero, done
lw t3, 0(t0) # carrega o elemento atual
add t2, t2, t3 # soma ao total
addi t0, t0, 4 # avança para a próxima palavra (4 bytes)
addi t1, t1, -1 # um a menos
j loop
done:
mv a0, t2 # imprime o total: 5+10+15+20 = 50
li a7, 1
ecall
li a7, 10
ecall

O loop funciona da seguinte forma. t0 contém o endereço de a, e t1 contém o número de elementos restantes, começando em 4. A cada passagem, encerra o loop quando a contagem chega a zero. carrega o elemento no endereço em t0. adiciona esse valor ao total acumulado. avança o ponteiro para a próxima palavra. diminui a contagem. Após quatro passagens (5 + 10 + 15 + 20), done imprime 50. Esse é o mesmo padrão usado na lição de memória, agora aplicado a um vetor.

Do índice ao endereço

A indexação calcula um endereço a partir de um ponteiro base e uma posição: address = base + index * 4. O 4 é o tamanho de uma palavra em bytes. Um compilador gera esse mesmo cálculo para a[i] em uma linguagem como C. O índice em si nunca acessa a memória. Apenas o endereço calculado a partir dele o faz.

Índice para endereço, usando um deslocamento
slli t5, t3, 2 # t5 = índice * 4 (tamanho de uma palavra)
add t6, t0, t5 # t6 = endereço de element[index]
lw t6, 0(t6) # t6 = element[index]

desloca o índice em t3 2 bits para a esquerda, o que o multiplica por 4 (dois dobros: 2 x 2), o tamanho de uma palavra. Somar o resultado ao ponteiro base dá o endereço de element[index]. Esse deslocamento é o mesmo método de potência de dois da lição de aritmética, e é a maneira padrão de escalar um índice pelo tamanho de uma palavra.

Cálculo de endereço
endereço=base+0×4=base+0
a[0]3
0x10010000
a[1]6
0x10010004
a[2]9
0x10010008
a[3]12
0x1001000c

a[0]: índice 0, deslocamento 0.

A barra do ponteiro e o selo hexadecimal se movem juntos enquanto o índice percorre 0, 1, 2, 3. Eles apresentam o mesmo fato em duas formas: i como uma posição, e o endereço que essa posição produz depois que o escala por 4 e o soma à base. Os números hexadecimais são os endereços reais que a ocupa quando o simulador executa este programa. O objetivo é prever o próximo endereço antes que ele apareça. Isso permite que você leia dentro de um loop incremental, ou a[i] em uma linguagem do tipo C, e visualize o endereço de memória que ele acessa em vez de tratar a notação como uma caixa preta.

Somando um vetor por índice

Some um vetor usando um índice
.data
a: .word 5, 10, 15, 20 # quatro elementos
.text
la t0, a # t0 = endereço base de a
li t1, 4 # t1 = número de elementos
li t2, 0 # t2 = índice, começa em 0
li t3, 0 # t3 = total acumulado
loop:
beq t2, t1, done
slli t4, t2, 2 # t4 = índice * 4 (deslocamento em bytes)
add t5, t0, t4 # t5 = endereço de a[index]
lw t5, 0(t5) # t5 = a[index]
add t3, t3, t5 # total += a[index]
addi t2, t2, 1 # avança para o próximo índice
j loop
done:
mv a0, t3 # imprime o total: 5+10+15+20 = 50
li a7, 1
ecall
li a7, 10
ecall

O loop funciona da seguinte forma. t2 é o índice, começando em 0. A cada passagem, encerra o loop quando o índice chega a 4, a contagem de elementos contida em t1. converte o índice em um deslocamento em bytes. soma esse deslocamento ao endereço base em t0 para produzir o endereço de a[index]. carrega o valor armazenado ali. Esse valor é somado ao total acumulado em t3, e avança para o próximo índice. Após quatro passagens (5 + 10 + 15 + 20), done imprime 50.

Somando dois vetores, elemento por elemento

c[i] = a[i] + b[i]
.data
a: .word 3, 6, 9, 12 # primeiro vetor
b: .word 1, 2, 3, 4 # segundo vetor
c: .space 16 # espaço para 4 palavras de resultado
.text
la t0, a # endereço base de a
la t1, b # endereço base de b
la t2, c # endereço base de c
li t3, 0 # índice, começa em 0
li t4, 4 # número de elementos
li s0, 0 # total corrente
loop:
beq t3, t4, done
slli t5, t3, 2 # deslocamento em bytes = índice * 4
add t6, t0, t5 # endereço de a[index]
lw t6, 0(t6) # valor de a[index]
add a0, t1, t5 # endereço de b[index]
lw a0, 0(a0) # valor de b[index]
add t6, t6, a0 # a[index] + b[index]
add a0, t2, t5 # endereço de c[index]
sw t6, 0(a0) # c[index] = a[index] + b[index]
add s0, s0, t6 # total += c[index]
addi t3, t3, 1 # avança para o próximo índice
j loop
done:
mv a0, s0 # imprime o total: 4+8+12+16 = 40
li a7, 1
ecall
li a7, 10
ecall

O loop funciona da seguinte forma. converte o índice t3 em um deslocamento em bytes. Somá-lo a cada ponteiro base (t0, t1, t2) dá os endereços de a[i], b[i] e c[i]. A cada passagem, ambos os elementos são carregados, somados, armazenados em c, e adicionados ao total acumulado em s0. avança para o próximo índice, e o loop se repete até que todos os quatro elementos sejam processados.

💡
Esse padrão de índice, base + index * tamanho do elemento, é como todo acesso a array é compilado, em qualquer linguagem. Tenha isso em mente: a próxima lição usa a mesma ideia duas vezes ao mesmo tempo para alcançar uma matriz.