Существует дополнительное возможное значение или значение для «доступа к массиву на основе указателя»:
У вас может быть указатель на массив, а не массив по фиксированному адресу. На самом деле, в C / C ++ «указатель на массив» обычно является просто указателем на первый элемент массива. По сути, у вас есть массив, который является параметром для функции, или указатель на массив, который является членом структуры или класса:
void Foo(char a[]);
/*or*/ void Foo(char *a);
struct Bar { int offset4bytes; char* a; };
Как правило, когда вы хотите использовать такой массив, базовый адрес массива будет загружен в регистр.
Теперь скажите, что вы хотите получить доступ к элементу i такого массива,
char tmp = a[i];
Допустим, r1 содержит адрес массива. (На самом деле, возможно, это другой регистр, заданный соглашением о вызовах для параметров функции. Все, что компилятор найдет доступным для другого кода.)
Допустим, я живу в регистре r2.
И, для хорошей меры, пусть tmp будет r3.
В некоторых наборах команд, например Intel x86, есть режим адресации, который выглядит как
MOV r3, (r2,r1)4
т.е. режим адресации может добавить два регистра и смещение (я произвольно добавил поле в пример структуры, чтобы показать это).
Черт, они могут даже масштабировать один из регистров, так называемый индексный регистр:
MOV r3, (r2*2,r1)4
или как я предпочитаю писать
r3 := load( Memory[r2<<1+r1+4]
MIPS, однако, не имеет такого рода режима адресации base + _index * scale + offset. Большинство инструкций доступа к памяти MIPS ограничены регистром + смещение. Поэтому для MIPS вам, возможно, придется сделать
ADDU r10, r1,r1 ; dest on left. r10 = 2*r1
ADDU r11, r2,r10
LB r3,(r11)4
т.е. вам может потребоваться добавить дополнительные инструкции RISC, чтобы выполнить действия x86 в одной инструкции CISC со сложным режимом адресации. Однако такая адресация не распространена, и часто ее можно избежать.
Кроме того, даже простая адресация массива по фиксированному адресу может потребовать дополнительных инструкций в MIPS. Инструкции x86 могут иметь 32-разрядное смещение памяти, которое в этом случае может фактически быть абсолютным адресом массива. Инструкции MIPS ограничены 16-битным смещением - инструкции MIPS имеют фиксированную ширину и ширину 32 бита. Поэтому отдельная инструкция может потребоваться даже для доступа к массиву по фиксированному адресу, обычно для загрузки старших бит адреса в регистр.
И еще - MIPS имеет более новые инструкции, такие как LUXC1, которые имеют режимы адресации reg + reg. Но не масштабированный индекс и не третий компонент смещения.
Из-за этого ограниченного режима адресации код, который наивный компилятор генерирует для цикла, такого как
for(int i=0;i<N;i++) {
this->a[i] = 0;
}
было бы неэффективно, если бы цикл содержал последовательность инструкций, упомянутую выше.
Петля, такая как
for(char *p=this->a;p<&(this->a[N]);p++) {
*p=0;
}
или, что эквивалентно
for(char *p=this->a;p<this->a+N;p++) {
*p;
}
или даже иногда
for(i=-N,*p=this->a;i<0;i++,p++) {
*p=0;
}
может быть более эффективным, потому что, например, в первых двух будет только одна инструкция для магазина. (Последний, как правило, выигрывает только при пересечении нескольких массивов.
Теперь в простом примере любой хороший компилятор сделает эту оптимизацию за вас. Но иногда компилятор предпочитает такой доступ на основе указателя.