文章目录
写在前面:从腾讯实习回来之后,就感觉到自己的知识体系过于散乱。于是萌生了写一个自己的操作系统这样的心思,此为系列第一章,主要是讲解一些汇编知识的,内容大多从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] 深入理解计算机系统 第三章