操作系统-内存篇

一 程序的内存分区

内存一般分为五个区:

1 堆区(stack)---由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈

2 栈区(heap)---一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。

3 全局区(static)---全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放

4 文字常量区---常量字符串就是放在这里的。 程序结束后由系统释放

5 程序代码区---存放函数体的二进制代码。

//main.cpp
int a = 0; //全局初始化区
int a = 0; //全局初始化区
char *p1; //全局未初始化区
main() {
    int b; //栈
    char s[] = "abc"; //栈
    char *p2; //栈
    char *p3 = "123456"; //123456\0在常量区,p3在栈上。
    static int c = 0; //全局(静态)初始化区
    p1 = (char *)malloc(10);
    p2 = (char *)malloc(20);
    //分配得来得10和20字节的区域就在堆区。
    strcpy(p1, "123456"); //123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}

 

 内存模型:

堆:

寄存器(栈)只能存放少量的数据,大多数时候, CPU 还要指挥寄存器直接跟内存交换数据,所以除了寄存器,还必须了解内存怎么存储数据。

程序运行的时候,操作系统会给它分配一块内存,用来存储程序和运行产生的数据,比如从 0x1000 到 0x8000,起始地址是较小的那个地址,结束地址是较大的那个地址。

程序运行过程中,对于动态的内存占用请求(比如新建对象),系统就会从预先分配好的那段内存中,划出一部分给用户,具体规则是从起始地址开始划分(实际上,起始地址会有一段静态数据,这里忽略)。举例来说,用户要求得到10个字节内存,那么从起始地址 0x1000 开始给他分配,一直分配到 0x100A,如果在要求得到 22 个字节,那么就分配到 0x1020。
 

 

这种因为用户主动请求而划分出来的内存区域,叫做 Heap(堆)。它由起始位置开始,从低位(地址)向高位(地址)增长。 Heap 的一个重要特点就是不会自动消失,必须手动释放,或者由垃圾回收机制来回收。

动态内存区域,使用alloc或new申请的内存;为了访问你创建在heap 中的数据,需要一个保存在stack中的指针,因为要通过stack中的指针访问heap 中的数据。

可以认为stack 中的一个指针仅仅是一个整型变量,保存了heap 中特定内存地址的数据。简而言之,操作系统使用stack 段中的指针值访问heap 段中的对象。如果stack 对象的指针没有了,则heap 中的对象就不能访问。这也是内存泄露的原因。

栈:

除了 Heap 外,其他的内存占用叫做 Stack(栈)。简单来说,Stack 是由于函数运行而临时占用的内存区域。

int main() {
    int a = 2;
    int b = 3;
}

上面代码中,系统开始执行 main 函数时,会为它在内存里面建立一个 帧(frame),所有的 main 的内部变量(比如 ab)都保存在这个 里面。main 函数执行结束后,该帧就会被回收,释放所有的内部变量,不再占用空间。

当主函数调用了其他函数:
 

int main() {
    int a = 2;
    int b = 3;
    return add_a_and_b(a,b);
}

 

 

所有的帧都存放在 Stack ,由于帧是一层层叠加的,所以 Stack 叫做 栈。生成新的帧,叫 入栈,英文单词是 push;栈的回收叫 出栈,英文是 pop。Stack 的特点就是,最晚入栈的帧最早出栈(因为最内层的函数调用,最先结束执行),这种叫做 后进先出 的数据结构。每一次函数执行结束,就自动释放一个帧,所有的函数执行结束,整个 Stack 就都释放了。

Stack 是由内存区域的结束地址开始,从高位(地址)向地位(地址)分配。比如,内存区域的结束地址是 0x8000,第一帧假定是 16 字节,那么下一次分配的地址就会从 0x7FF0 开始;第二帧假定需要 64 字节,那么地址就会移到 0x7FB0。

二 代码段、数据段、bss段:

首先看一个例题,以下分配在BSS段的变量是?

char s1[100];
int s2 = 0;
static int s3 = 0;
 
int main() {
    char s4[100];
}

答案是:

S1

  1. 代码段(Code Segment 或 Text Segment)对应于内存分区中的可执行代码区域,存储程序的指令和函数定义。

  2. 数据段(Data Segment)对应于内存分区中的已初始化全局变量和静态变量的区域。

  3. BSS段对应于内存分区中的未初始化的全局变量和静态变量的区域。

三 动态内存函数

 NEW和malloc

1.new内存分配失败时,会抛出bac alloc异常,它不会返回NULL; malloc分配内存失败时返回NULL;

2.使用new操作符申请内存分配时无须指定内存块的大小,而malloc则需要显式地指出所需内存的尺寸;

3.opeartor new /operator delete可以被重载,而malloc/free并不允许重载。;

4.new/delete会调用对象的构造函数/析构函数以完成对象的构造/析构。而malloc则不会;

5.malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符;

6new操作符从自由存储区上为对象动态分配内存空间,而malloc函数从堆上动态分配内存.

四 柔性数组

C99中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员,但结构中的柔性数组成员前面必须至少有一个其他成员。

    struct S
    {
    	int num;
    	double d;
    	int arr[];//柔性数组成员
    };

4.1 柔性数组的特点

1.结构中的柔性数组成员前面必须至少一个其他成员;

2.sizeof返回的这种结构大小不包括柔性数组的内存;

3.包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

struct S3
{
	int num;
	int arr[0];//柔性数组成员
};
 
int main()
{
	printf("%d\n",sizeof(struct S3));//4
 
	return 0;
}

4.2 柔性数组的使用

S3
{
	int num;
	int arr[0];//柔性数组成员
};
 
int main()
{
	printf("%d\n",sizeof(struct S3));//4
 
	//开辟空间
	struct S3* ps = (struct S3*)malloc(sizeof(struct S3) + 40);//40用于柔性数组成员
 
	//判空
	if (ps == NULL)
	{
		perror("malloc");
		return 1;
	}
 
	//使用
	ps->num = 100;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		ps->arr[i] = i;
	}
 
	//打印
	for (i = 0; i < 10; i++)
	{
		printf("%d ",ps->arr[i]);
	}
 
	//扩容
	struct S3* ptr = (struct S3*)realloc(ps, sizeof(struct S3) + 80);
 
	//判空
	if (ptr == NULL)
	{
		perror("realloc");
		return 1;
	}
	else
	{
		ps = ptr;
	}
 
	//使用
	for (i = 10; i < 20; i++)
	{
		ps->arr[i] = i;
	}
 
	//继续打印
	for (i = 10; i < 20; i++)
	{
		printf("%d ", ps->arr[i]);
	}
 
 
	//释放
	free(ps);
	ps = NULL;
 
	return 0;
}

4.3 柔性数组的优势

方便内存释放;

有利于访问速度。

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值