该二维数组在内存中用一维数组索引表示为:
为了计算我们需要的元素地址,首先,第一个索引乘以4(矩阵宽度),然后加上第二个索引。这种被称为行优先,C/C++和Python常用这种方法。行优先的意思是:先写入第一行,接着是第二行,…,最后是最后一行。 另一种方法就是列优先,主要用在FORTRAN,MATLAB,R等。列优先的意思是:先写入第一列,然后是第二列,…,最后是最后一列。 多维数组与此类似。 我们看个例子:
Listing 16.4: simple example
int a[10][20][30];
void insert(int x, int y, int z, int value)
{
a[x][y][z]=value;
};
Listing 16.5: MSVC 2010
多维数组计算索引公式:address=6004x+304y+4z。因为int类型为32-bits(4字节),所以要乘以4。
Listing 16.6: GCC 4.4.1
public insert
insert proc near
x = dword ptr 8
y = dword ptr 0Ch
value = dword ptr 14h
push ebp
push ebx
mov ebx, [ebp+x]
mov eax, [ebp+y]
mov ecx, [ebp+z]
lea edx, [eax+eax] ; edx=y*2
mov eax, edx ; eax=y*2
shl eax, 4 ; eax=(y*2)<<4 = y*2*16 = y*32
sub eax, edx ; eax=y*32 - y*2=y*30
imul edx, ebx, 600 ; edx=x*600
add eax, edx ; eax=eax+edx=y*30 + x*600
lea edx, [eax+ecx] ; edx=y*30 + x*600 + z
mov eax, [ebp+value]
pop ebx
retn
insert endp
Listing 16.7: Non-optimizing Xcode (LLVM) + thumb mode
非优化的LLVM代码在栈中保存了所有变量,这是冗余的。元素地址的计算我们通过公式已经找到了。
Listing 16.8: Optimizing Xcode (LLVM) + thumb mode
_insert
MOVW R9, #0x10FC
MOV.W R12, #2400
MOVT.W R9, #0
RSB.W R1, R1, R1,LSL#4 ; R1 - y. R1=y<<4 - y = y*16 - y = y*15
ADD R9, PC ; R9 = pointer to a array
LDR.W R9, [R9]
MLA.W R0, R0, R12, R9 ; R0 - x, R12 - 2400, R9 - pointer to a. R0=x*2400 + ptr to a
ADD.W R0, R0, R1,LSL#3 ; R0 = R0+R1<<3 = R0+R1*8 = x*2400 + ptr to a + y*15*8 =
; ptr to a + y*30*4 + x*600*4
STR.W R3, [R0,R2,LSL#2] ; R2 - z, R3 - value. address=R0+z*4 =
很多函数参数的输入标志使用了位域。当然,可以使用bool类型来替代,只是有点浪费。