汇编 —— 特殊数据结构

文章目录

写在前面:从腾讯实习回来之后,就感觉到自己的知识体系过于散乱。于是萌生了写一个自己的操作系统这样的心思,此为系列第一章,主要是讲解一些汇编知识的,内容大多从CSAPP中也可以获得。
本篇内容主要讲解:结构体和联合体的汇编实现

结构体

C语言中用struct来声明一个结构体,将可能不同类型的对象聚合到一个对象指针,用名字来引用结构的各个组成部分。

像数组一样,结构体的所有组成部分都存放在内存中一段连续的区域内,而指向结构的指针就是结构第一个字节的地址。编译器维护关于每个结构类型的信息,指示每个字段的字节偏移。同时,它以这些偏移作为内存引用指令中的唯一,从而产生对结构元素的引用。

让我们来看下面这个例子,考虑这样的结构声明:

struct rec
{
    int i;
    int j;
    int a[2];
    int *p;
};

这个结构体在内存中的摆放便如下所示:
汇编 —— 特殊数据结构
为了访问结构的字段,编译器产生的代码要将结构的地址加上适当的偏移。例如,假设struct rec*类型的变量 r 放在寄存器%rdi中,那么下面的汇编代码,会让r->j = r->i

movl	(%rdi), %eax			//%rax = r->i
movl	%eax, 4(%rdi)			//r->j = %rax

那么我们再复杂一点,计算下面这里表达式的汇编代码:

r->p =&r->a [r->i+r->j];

其汇编代码如下:

//Registers: r in %rdi
movl	4(%rdi), %eax				//%rax = r->j
addl	(%rdi), %eax				//%rax += r->i
cltq								//扩展成 8字节
leaq	8(%rdi,%rax,4), %rax		//%rax = %rax*4 + %rdi + 8-->&r->a[r->i+r->j]
movq	%rax, 16(%rdi)				//r->p = %rax

练习题

汇编 —— 特殊数据结构

练习题答案

汇编 —— 特殊数据结构

int result = 0;
while(ptr != nullptr)
{
	result += ptr->val;
	ptr = ptr->next;
}
return result;

联合体

联合提供了一种方式,能够规避C语言的类型系统,允许以多种类型类似引用一个对象。比如下面的联合体声明:

typedef union U3{
	struct{				//这个结构体大小11
		long u;
		short v;
		char w;
	}t1;
	struct{				//这个结构体大小16字节
		int a[2];
		char *p;
	}t2;
}up;

而这个类型在内存中便只有最长的元素的空间:
汇编 —— 特殊数据结构
其访问跟结构体一样,直接使用偏移量来表示各个元素,这里就不举例了。

数据对齐

许多计算机体系对基本数据类型的合法地址做出了一些限制,要求某种类型的对象的地址必须是某个值K(K = 2、4、8)的倍数,而这就是内存对齐!

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

而在汇编中,我们可以使用下面的命令指定内存对齐方式:

.align 8		//8字节内存对齐

而如果按照8字节来对齐的,下面的数据结构在内存中就会改变其内存数据。

struct S
{
	int i;
	int j;
	char c;
};

汇编 —— 特殊数据结构
那么假如说程序定义为这样呢?

struct S
{
	int i;
	char c;
	int j;
};

程序布局便成为如下的布局了
汇编 —— 特殊数据结构

参考文献

[1] 深入理解计算机系统 第三章
上一篇:编译过程中遇到的问题


下一篇:C++多态原理