深入理解计算机操作系统——第3章:数组,异质的数据结构,指针
3.8 数组的分配和访问
(1)基本原则:
int A[10]
首先,它在存储器中分配一个sizeof(int)*10字节的连续区域。
其次,它引入了标识符A。可以用A作为指向数组开头的指针。
若是用xA来表示数组在内存中的起始位置,数组存放在xA+sizeof(int)*10的连续区域中
(2)嵌套数组
数组元素的存储采用行优先的原则。
int A[5][3]
将A看做有5个元素的数组,A[0] A[1] A[2] A[3] A[4]
而其中每个元素又是含有三个元素的数组。
元素A[i][j]的地址:编译器是这样计算的&A[i][j]=xA+sizeof(int)*(行数*i+j) 这其中xA为起始地址
3.9 异质的数据结构
(1)结构体:struct
结构体的所有组成部分都存储在存储器的一段连续的内存区域内,而指向结构体的指针为结构体第一个字节的地址。
编译器维护每个关于每个结构体类型的信息,指示每个字段的偏移。
(2)联合体 union
联合提供了一种方式,能够规避C语言的类型系统,允许以多种类型来引用一个对象。
一个联合体的总的大小为它的最大的字段的大小。
如果一个结构体中两个类型是互斥的,那么可以用联合体来表示,从而可以节省内存空间。
下面是一个例子:
假设二叉树的只有叶子结点有数据,而上面的结点只有指向左右子树的指针,
1 struct TreeNode{ 2 TreeNode* left; 3 TreeNode* right; 4 double val; 5 };
上面4+4+8=16个字节,而只有叶子结点处才需要double,可以使用联合体来做
1 union TreeNode_U{ 2 struct{ 3 TreeNode_U* left; 4 TreeNode_U* right; 5 }inter; 6 double val 7 };
而上面就只需要8个字节来存储,但是不知道一个结点是否是叶子,所以还要加上枚举类型就可以只使用12个字节
1 typedef enum{TERNANL,TERNAL_N} ENUM_NODE; 2 union TreeNode_U{ 3 ENUM_NODE typeinf;//4个字节 4 struct{ 5 TreeNode_U* left; 6 TreeNode_U* right; 7 }inter;//4个字节 8 double val//8个字节 9 };
(3)数据对齐
数据对齐可以提高存储器的性能
即某对象的地址必须是2,4,8的整数倍。
在linux中一般short地址必须是2的倍数,较大的数据类型(int int* float double)的地址必须是4的倍数
例如:
1 #include<iostream> 2 using namespace std; 3 struct S1 4 { 5 int i;//4字节 6 char c;//1字节 7 int j;//4字节 8 }; 9 struct S2 10 { 11 int i;//4字节 12 int j;//4字节 13 char c;//1字节 14 }; 15 int main() 16 { 17 S1 a; 18 cout<<sizeof(a)<<endl;//12字节 19 S2 b; 20 cout<<sizeof(b)<<endl;//12字节 21 system("pause"); 22 }
3.10 理解指针
每个指针都对应着一个类型,这个类型表明指针指向哪一类对象
(1)特殊的void*类型代表通用指针‘
(2)每个指针都有一个值:这个值是某一特定对象的地址,特殊的NULL(0)指针是表示该指针没有指向任何地方
(3)将一个指针从一种类型强制改变成另外一种类型只是改变它的类型,而不改变它的值
(4)函数指针的值表示的是该函数的机器代码中的第一条指令存放的地址
int (*f)(int*)表示f为一个函数指针,这个函数的参数为int*,返回值为int
(5)数组与指针紧密联系,一个数组的名字可以像指针变量一样引用(但不可以修改)。