深入理解计算机系统笔记

1.1 嵌套的数组

当我们创建数组的数组时,数组分配和引用的一般原则也是成立的。

例如,声明 int A[5][3];

等价于下面的声明

typedef int row3_t[3];

row3_t A[5]

要访问多维数组的元素,编译器会以数组起始为基地址, (可能需要经过伸缩的)偏移量为索引,产生计算期望的元素的偏移量,然后使用某种 MOV指令。通常来说,对于一个 声明如下的数组:

T D[R][C];

它的数组元素D[i][j]的内存地址为

&D[i][j]= Xd +L(C*i+ j)

这里L是数据类型T以字节为单位的大小。

1.2定长数组

C语言编译器能够优化定长多维数组上的操作代码。这里我们展示优化等级设置为 -01时GCC采用的一些优化。假设我们用如下方式将数据类型fixjnatrix声明为 16X16 的整型数组:

#define N 16;

typedef int fix_matrix[N][N];

这样做的好处是,如果需要修改这个值只需要简单的修改这个# define声明就可以了。

1.3 变长数组

历史上,C语言只支持大小在编译时就能确定的多维数组(对第一维可能有些例外)。 程序员需要变长数组时不得不用malloc或calloc这样的函数为这些数组分配存储空间,而且不得不显式地编码,用行优先索引将多维数组映射到一维数组,ISO C99引入了一种功能,允许数组的维度是表达式,在数组被分配的时候才计算出来。

在变长数组的C版本中,我们可以将一个数组声明如下:

int  A[expr1] [expr2];

它可以作为一个局部变量,也可以作为一个函数的参数,然后在遇到这个声明的时候,通过对表达式和求值来确定数组的维度。

因此,例如要访问 n *n 数组的元素 I ,j,我们可以写一个如下的函数:

int var_ele(long n, int A[n][n], long i, long j){

return A[i][j]; }

参数n必须在参数A[n][n]之前,这样函数就可以在遇到这个数组的时候计算出数组的维度。

GCC为这个引用函数产生的代码如下所示:

int var_ele(long n, int A[n][n], long i, long j)

n in %rdi, A in %rsi, i in %rdx, j in %rcx

1  var_ele:

2  imulq %rdx, %rdi

3  leaq (%rsi,%rdi*4), %rax

4  movl (%rax,%rcx,4), %eax

5  ret

这 个地址的计算类似于定长数组的地址计算,不同点在于

1)由于增加了参数 n, 寄存器的使用变化了;

2)用了乘法指令来计算n * i(第2行),而不是用leaq指令来计算3i 。因此引用变长数组只需要对定长数组做一点儿概括。

动态的版本必须用乘法指令对 i 伸缩n倍,而不能用一系列的移位和加法。在一些处理器中,乘法会招致严重的性能处 罚,但是在这种情况中无可避免。

2.1异质的数据结构

C语言提供了两种将不同类型的对象组合到一起创建数据类型的机制:结构(structure),用关键字 struct 来声明,将多个对象集合到一个单位中;联合(union),用关键 字union 来声明,允许用几种不同的类型来引用一个对象

2.2结构

C语言的struct声明创建一个数据类型,将可能不同类型的对象聚合到一个对象中。 用名字来引用结构的各个组成部分。类似于数组的实现,结构的所有组成部分都存放在内存中一段连续的区域内,而指向结构的指针就是结构第一个字节的地址。编译器维护关于每个结构类型的信息,指示每个字段(field)的字节偏移。它以这些偏移作为内存引用指令中的位移,从而产生对结构元素的引用。

2.3联合

联合提供了一种方式,能够规避C语言的类型系统,允许以多种类型来引用一个对 象。联合声明的语法与结构的语法一样,只不过语义相差比较大。它们是用不同的字段来引用相同的内存块。

考虑下面的声明: struct S3{

char c;

int i[2];

double v;

};

union U3{

char c;

int i[2];

double v; 

};

在一台x86-64Lmux机器上编译时,字段的偏移量、数据类型S3和U3的完整大小如下:

在一些下上文中,联合十分有用。但是,它也能引起一些讨厌的错误,因为它们绕过 了C语言类型系统提供的安全措施。一种应用情况是,我们事先知道对一个数据结构中的两个不同字段的使用是互斥的,那么将这两个字段声明为联合的一部分,而不是结构的一 部分,会减小分配空间的总量。

2.4数据对齐

许多计算机系统对基本数据类型的合法地址做出了一些限制,要求某种类型对象的地 址必须是某个值K(通常是2、4或8)的倍数。这种对齐限制简化了形成处理器和内存系统 之间接口的硬件设计。

例如,假设一个处理器总是从内存中取8个字节,则地址必须为8 的倍数。如果我们能保证将所有的double类型数据的地址对齐成8的倍数,那么就可以 用一个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中。

对齐原则是任何K字节的基本对象的地址必须是K•的倍数。可以 看到这条原则会得到如下对齐:

确保每种数据类型都是按照指定方式来组织和分配,即每种类型的对象都满足它的对齐限制,就可保证实施对齐。编译器在汇编代码中放人命令,指明全局数据所需的对齐。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值