c\c++ 进程内存分布详解

首先理论知识

一个程序调用的内存主要被划分为4个区域,分别是栈区,堆区,数据区,代码区
首先来讲讲栈区:

  • 栈区采用先进后出的数据结构,这也是为什么栈区叫栈区
  • 自动局部变量、自动函数形参的内存空间都来自这个区域
  • 而这个内存空间是在执行到变量定义语句才分配的,如果分配失败程序就崩溃了
  • 而栈区的大小是受系统限制的,所以递归没有出口时就会一直占用栈区内存空间,直到栈区空间占满,造成栈溢出,使程序崩溃,所以递归效率低
  • 一次隶属复合语句执行完空间就失效,下次复合语句被执行时空间在重新分配
    (比如:调用一个函数,然后这个函数的自动局部变量已经函数的形参入栈,然后函数调用完毕后,这个函数在栈区的空间就被系统回收了(其实在汇编就是栈顶指针的偏移 ))
  • 所以栈区内的内存空间的生存区由系统来设置,故称栈区内存空间的生存区为自动储存时期

然后我们来说堆区:

  • 栈区的空间是由系统来支配的,那么堆区的空间就是由我们来支配了,欸嘿嘿
  • 正因为堆区的空间是由我们来支配,所以堆区的内存空间的生存区为动态储存时期
  • 由我们调用malloc函数或者在c++里可以用new运算符new出来(也就是向系统申请空间)
    注意:刚分配出来的空间是野值,不可以直接使用
  • 然后我们不用这块内存的时候就要用free函数(如果是malloc出来的)或者delete(new出来的)把其释放掉(即通知系统来回收)
  • 这里要特别注意:!!!如果不用了一定要释放该内存空间!!!否则将造成内存泄漏,后果很严重!!!(对系统来说,就相当于别人向你借了100万然后还不还一样
  • 还要注意一定:同一块内存不要释放两次(难道你想向别人借100万然后还200万 233,那么请务必向我借钱

接下来是数据区:

  • 数据区顾名思义,就是用来储存数据的
  • 数据区又可化分为3个小区,分别是BSS段,普通数据区,只读数据区
  • 其中BSS段储存着未初始化的或初始化为0的全局变量和static局部变量
  • 数据区就储存着初始化为非零的全局变量和static局部变量(从这里就可以看出带了static关键字的局部变量储存方式其实完全和全局变量一样,只不过,它只可以在声明这个变量的空间内起作用)
  • 所以要注意可读写的static变量要谨慎使用
  • 然后只读数据区也是顾名思义,代表该区域的数据是只读的,不能被修改,所以一般字符串常量、带const的全局变量和static的局部变量
  • 举个栗子
//在main函数内
char * str = "hallo";	//这里用一个指针变量str的内存空间存放在栈区里面占了sizeof(char *)个字节,而系统还分配了6个字节的空间给"hallo"字符串,这个字符串储存在数据区中的只读数据区
str[2] = 'a';			//这里如果我们修改字符串内存,编译器就会报错,所以这也说明了只读数据区是只读的。

  • 但是如果是const修饰的全局变量我们去修改是可以通过编译的,编译器只是会给我们一个警告,但是程序运行起来就会崩溃了,所以我们千万不要去修改只读数据区的变量
  • 举个栗子
#include <stdio.h>
const int x = 90;
int main()
{
	int *ptr = &x;
	*ptr = 233;	//此时编译是可以通过的,但是程序运行到这句代码时就会崩溃
				//所以我们尽量不要用指针去指向一个只读数据区的常量,如果实在要指向,一定要加上const关键字
				//比如这样 const int *ptr = &x;	
				//此时如果在意图以指针间接修改x空间内的值,编译器将会报错
	return 0;
}
  • 所以,总结出只读数据区不能有写操作,有了直接崩溃
  • 数据区还有以下特点
  1. 该区域里的内存空间在程序运行前期(在进入main函数之前)分配(如果内存不足程序无法运行),所以全局数据区不会有野值,如果变量未初始化将自动置为零然后储存在BSS段,如果加了const关键字就会储存在只读数据区
  2. 该区域里的内存空间在程序退出运行后释放,故可读写的全局变量不推荐使用
  3. 前一次的修改维持到下一次使用

然后,我们在来说说代码区:

  1. 顾名思义,所有函数对应的二进制指令均存放在本区域
  2. 所以的指令以函数为单位在本区域进行组织存放,程序中有多少函数,本区域就被分成多少份,没一份对应一个函数,因此函数名实际也代表函数在本区域所占空间的首地址
  3. 任何非NULL的函数指针均指向本区域的某一个函数
  4. 本区域只读,可执行
  5. 和数据区一样都是程序运行前分配(main函数以前),所以本区域的生存期为静态储存时期(也就是程序运行全期)

理论知识过后,我们来实践一下

我们来通过地址来看看,进程的内存分布

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char test1 = '1';           //数据区
char test5 = '5';           //
const char test2 = '2';     //数据区只读数据区
char test3;                 //数据区BSS段
void Code();


int main(int argc, char **argv)
{
    system("color a");
    static char test4 = 0;
    char *ptr = "abcdefg";
    char *ptr_h = (char *)malloc(1);
    printf("\n----------------------------------------------------------\n\n");
    printf("代码区:\n");
    printf("main函数地址 %p\n", main);
    printf("Daima函数地址: %p\n", Code);
    printf("\n----------------------------------------------------------\n\n");
    printf("栈区:\n");
    printf("ptr的地址 %p\n", &ptr);
    printf("ptr_h的地址 %p\n", &ptr_h);
    printf("\n----------------------------------------------------------\n\n");
    printf("数据区:\n");
    printf("test1变量的存放空间 %p\n", &test1);
    printf("test5变量的存放空间 %p\n", &test5);
    printf("\n------------------------只读数据区---------------------\n");
    printf("test2变量的存放空间 %p\n", &test2);
    printf("\"abcdefg\"的存放空间 %p\n", ptr);
    printf("\n----------------------bss段------------------------------\n");
    printf("test3变量的存放空间 %p\n", &test3);
    printf("test4变量的存放空间 %p\n", &test4);
    printf("\n----------------------------------------------------------\n\n");
    printf("堆区:\n");
    printf("malloc申请来的地址 %p\n", ptr_h);
    printf("\n----------------------------------------------------------\n\n");
    free(ptr_h);

    return 0;
}

void Code()
{
    printf("wakaka!");
}

程序运行结果
运行结果
接下来我们来验证,BSS段是不是存放了初始化为0的全局变量或局部变量
我们去修改test4的初始化
不给定初值
运行结果
验证
然后我们将test4初始化为非0;
验证
运行结果
结果

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值