WARNING这是《深入理解计算机系统》(CS
)的笔记,彼时还未上CO,OS等计算机系统基础课,存在理解不到位与错误之处.
本质都是一片连续的内存空间(包括多维数组)。注意联合是用不同的方式“解释”同一串二进制数。
多维数组 D[M][N] 有地址偏移量计算(这就是编译器中使用的访问方法):
注意头指针后面那个是偏移量,L是每个元素的字节数。
优化编译器擅长对定长数组(尤其是二维数组)的遍历进行优化。比如计算:
//计算中矩阵乘法中的一个元素
int D[M][N],E[N][S],i,j,k;
int result=0;
for(k=0;k<N;k++){
result+=D[i][k]*E[k][j];
}
会进行如下优化:提前定位 D 第 i 行的头指针,然后每次将这个指针 += 4(sizeof(int));提前定位 E 中第 0 行第 j 列的地址,然后每次将这个地址 += S * 4。这样避免了每次访问二维数组中的元素都用上面的公式。
结构体
结构中有不同类型的数据,为了提高内存访问效率,往往需要数据对齐。规则是:任意一个数据类型的数据,其地址值必须是这个数据类型所占的字节数(sizeof)的整数倍。,不满足,就要中间添加空位置。
不只是结构,任何数据都是如此。只是结构更集中。
如:
struct Si{
int a;
char c;
int b;
}
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
---|---|---|---|---|---|---|---|---|---|---|---|
a | a | a | a | c | 空 | 空 | 空 | b | b | b | b |
注意中间那几个空。
实际上地址不可能从0开始,结构的第一个元素当然也需要满足数据对齐,所以隐含地对结构有了数据对齐的要求,如果是在结构数组中,就十分明显。
大部分X86-64指令其实不需要数据对齐也能正确执行。但有些Intel、AMD的产品对于跨媒体的指令(16字节数的操作,如全乘法)(SSE寄存器)就必须是数据16字节对齐。这要求任何任何可能涉及到SSE寄存器处理的数据结构的内存都要16字节对齐,这要求:
1)内存动态分配函数分配的空间地址要16字节对齐。
2)大多数函数栈帧的边界地址需要是16字节的倍数。
可变长栈帧
C支持了可变长数组,即数组声明中的[ ]可以为变量或表达式,或者动态内存分配,这样编译器无法事先确定分配的栈帧空间的大小。
这似乎只是在分配数组空间前进行变量计算的事,但由于涉及到对齐,机器代码中会有很多奇怪的位运算等,来实现16字节对齐(前面说过),并在数组起始实现数据类型对齐。(如 &= -16 来向下取整为16的整数倍,>>3*8向下取整为8的整数倍等等。
另外,在正式开始执行本过程前(准确地说,是刚从父过程跳转过来,还没分配栈帧,更没保存被调用者保存寄存器上的值的时候),需要先将 %rbp 中的值保存在栈中,然后把此时%rsp保存到%rbp中,这样%rbp就是父过程的返回地址(-1)的指针,称为栈帧指针,或者基指针(base pointer)。变长栈帧中需要提前维护这一个基指针。