目录 Table of Contents
数组的访问
一个例子:
typedef int zip_dig[5];
zip_dig cmu = {1, 5, 2, 1, 3};
zip_dig mit = {0, 2, 1, 3, 9};
zip_dig ucb = {9, 4, 7, 2, 0};
typedef int zip_dig[5]
表示用 zip_dig 代替 int[5], 即zip_dig a
等于int a[5]
https://www.runoob.com/cprogramming/c-typedef.html
我们定义了一个数据类型, 它相当于 5 个长度为 int 的这么一个数组的类型, 然后拿它定义了 3 个具体的数组实例
假设我们用寄存器 %edx 来存储数组的起始地址, %eax 表示你所要访问元素的下标
那么对应数组的内存地址就很明确了, 就是你的起始地址加上你的 index * sizeof, 这个和之前的一个寻址模式很类似, 也就是和地址计算模式类似.
所以写出来的指令如下 :
x86-32 下的数组访问代码
; %edx = x
; %eax = dig
movl (%edx, %eax, 4), %eax ; z[dig]
数组循环示例 (x86-32)
void zincr(zip_dig z)
{
int i;
for (i = 0; i < 5; i++)
z[i]++;
}
我们把刚才那个数组, 长度为 5, 每个数据类型都是 int, 把它作为一个循环, 循环把每一个元素加加
它的循环, 对应出来的汇编代码如下
; edx = z
movl $0, %eax ; %eax = i
.L4:
addl $1, (%edx, %eax, 4) ; z[i]++
addl $1, %eax ; i++
cmpl $5, %eax ; i : 5
jne .L4 ; if !=, goto loop
我们把这个循环代码变形一下, 刚才是用数组下标来访问, 现在我们用指针来访问
void zinvr_p(zip_dig z)
{
int *zend = z + 5;
do{
(*z)++;
z++; z 为数组 0 号元素的地址, 这个相当于往下一个元素移动的这么个逻辑
}while (z != zend);
}
转换成更加直观的 C++ 代码:
void zincr_v(zip_dig z)
{
void *vz = z;
int i = 0;
do{
(*((int *) (vz + i)))++;
i += ISIZE; /* sizeof(int)*/
}while (i != ISIZE * 5)
}
汇编代码如下 :
; edx = z = vz
movl $0, %eax ; i = 0
.L8:
addl $1, (%edx, %eax) ; increment vz + i
addl $4, %eax ; i += 4
cmpl $20, %eax ; compare i : 20
jne .L8 ; if !=, goto loop
嵌套数组
就是二维数组, 它是按照行来存储, 第 i 行和第 i + 1 行是连续存储的, 然后每一行有若干个元素, 这若干个元素是连续存储的.
访问嵌套数组中的 "行"
int *get_pgh_zip(int index)
{
return pgh[index];
}
至于这种二维的或者说嵌套数组, 怎么进行访问, 那么首先就是讲讲怎么访问嵌套数组里面的这一行
比如这个二维数组, 我要访问里面 index 这一行. index 它这个 pgh 的数据类型是 int[5], 即 pgh 每个元素 (里层的数组) 的大小是 5*sizeof(int) = 20
所以行地址, 也就是它的起始地址加上你每一个元素, 实际上这个元素是一个数组, 也就是外层数组的元素, 这个数组的 size 乘上你的 index, index 就是你要访问的那个行的下标, pgh + (20 * index)
相关汇编代码 pgh + 4 * (index + 4 * index)
; %eax = index
leal (%eax, %eax, 4), %eax ; 5 * index
leal pgh(,%eax, 4), %eax ; pgh + (20 * index)
访问嵌套数组的单个元素 (里层元素)
int *get_pgh_digit(int index)
{
return pgh[index][dig];
}
pgh[index][dig] 的地址是 : pgh + 20 * index + 4 * dig
相关汇编代码 pgh + 4 * dig + 4 * (index + 4 * index)
还是尽量使用 leal
这种方式来进行地址的计算
; ecx = dig
; eax = index
leal 0(, %ecx, 4), %edx ; 4 * dig
leal (%eax, %eax, 4), %eax ; 5 * index
movl pgh(%edx, %eax, 4), %eax ; *(pgh +4 * dig + 20 * index)
Multi-Level Array 多层数组
我们定义了一个指针数组, 数组长度为 3, 数组每一个元素是一个指针, 长度为 4 字节.
每一个指着又指向了一个 长度为 5 的一个 int 类型数组
zip_dig cmu = {1, 5, 2, 1, 3};
zip_dig mit = {0, 2, 1, 3, 9};
zip_dig ucb = {9, 4, 7, 2, 0};
我们已经有了 3 个一维的数组, 它们地址分别是 16, 36, 56. 注意到这三个数组在内存中是连续存放的, 这个是有可能的.
#define UCOUNT 3
int *univ[UCOUNT] = {mit, cmu, ucb};
然后我们把这三个数组的地址填到 Multi-Level 这个数组里面
访问 Multi-Level Array 中的元素
这个要进行数据元素的计算, 完全和刚才那个嵌套的二维数组不一样, 刚才那个嵌套数组是连续存储的, 这个只是有可能是连续存储的, 它每一行实际上完全没有什么关系
int get_univ_digit(int index, int dig)
{
return univ[index][dig];
}
现在我们不能像之前嵌套数组那样取地址了, 我们先要把行地址取到, 然后再进行访问内存, 把行地址里面这个元素提取出来
所以它要进行数据运算的话, 用伪代码的形式是这样的 Mem[Mem[univ + 4 * index] + 4 * dig]
; %ecx = index
; %eax = dig
leal 0(, %ecx, 4), %edx ; 4 * index
movl univ(%edx), %edx ; Mem[univ + 4 * index] 首先访问第一维数组, 把指针里面指向的内容取出来, 就是你要访问的那行数据的首地址取出来
movl (%edx, %eax, 4), %eax ; Mem[... + 4 * dig] 然后进行第二次访问内存, 取那个实实在在的那个元素