【C语言】-- 一篇带你了解C语言内存五大区——栈区,堆区,全局区,常量区,代码区

目录

1 C语言的内存分区

1.1 内存五大分区

1.2 内存分区简介

1.2.1 栈区(stack)

1.2.2 堆区(heap)

1.2.3 (全局)静态区

1.2.4 常量区

1.2.5 代码区

创作不易,如果本篇博客对您有一定的帮助,大家记得留言+点赞哦。


C语言已经持续学习一段时间了,今天特此总结一下关于C语言内存的五大区。它是我们深入理解C语言非常有必要了解的知识点。通过了解五大区,对于进一步学习C语言底层是非常有帮助的。

1 C语言的内存分区

1.1 内存五大分区

C语言内存可大致分为5个区域,图和表如下:

内存影像区内容 权限
栈区函数中的普通变量可读可写
堆区动态申请的内存可读可写
(全局)静态变量区static修饰的变量可读可写
常量区用于初始化变量的常量只读
代码区代码指令只读

nt k=1;
void main()
{
	int i=1;
	char *j;
	static int m=1;
	char *n="hello";
 
	printf("栈区地址为:0X%x\n",&i);		
	j = (char*)malloc(2); //一般不确定需要多大空间的时候用
	free(j);//及时释放
	printf("堆区地址为:0X%x\n",j);
	printf("全局变量地址为:0X%x\n",&k);
	printf("静态变量地址为:0X%x\n",&m);
	printf("文字常量区地址为:0X%x\n",n);
	printf("程序区地址为:0X%x\n",&main);
}
 
	char *i="hello";
	char j[10]="hello";
	printf("0X%x\n",i); //存放在文字常量区
	printf("0X%x\n",j); //存放在栈区
	j[1]='*';//可以直接赋值
	//*(i+1)='*'; //等价于i[1]='*';
	//不可以这样赋值, 因为i是指针,指向的是文字常量区,里面的内容是不能修改的
	i=j; //这样可以
	printf("%s\n",i);
	printf("%x\n",i);
	j=i;//这样不可以,因为j虽然也是地址,但是不是指针变量,不能直接赋值

1.2 内存分区简介

1.2.1 栈区(stack)

栈区由编译器自动分配释放,由操作系统自动管理,无须手动管理。
栈区上的内容只在函数范围内存在,当函数运行结束,这些内容也会自动被销毁。

#include<stdio.h>
char *getMem()
{
	char buf[64]; //局部变量 栈区存放
	strcpy(buf, "123456789");//向buf所代表的内存中写入内容
	//printf("buf:%s\n", buf);
	return buf;//返回所分配内存区域的第一个元素的地址
}
 
void main()
{
	char *tmp = NULL;
	tmp = getMem(10);
	if (tmp == NULL)
	{
		return ;
	}
	printf("tmp:%s\n", tmp);//输出tmp:
	system("pause");
	return ;
}

内存分析:

      

 

  • 栈区按内存地址由高到低方向生长,其最大大小由编译时确定,速度快,但自由性差,最大空间不大。
  • 栈区是先进后出原则,即先进去的被堵在屋里的最里面,后进去的在门口,释放的时候门口的先出去。
  • 栈区存放内容

  • 临时创建的局部变量const定义的局部变量存放在栈区。
  • 函数调用和返回时,其入口参数返回值存放在栈区。

通俗来说:

栈区是用来存放局部变量的,比如函数内部定义的int a,int b,const int a,char p,char arr[ ],还有函数的形参等等都是存放在栈区。栈区的数据由编译器管理,调用完之后就自动释放,压栈,出栈。先进后出的原则,比如当你执行到函数调用的时候,编译器会先把下一条代码的地址压入栈中,再把你调用的那个函数里的一些局部变量啊,形参啊等等压入栈中,等你函数调用执行完毕。栈就会把你调用的这个函数之前压入栈的变量和形参全部清除出栈,之后根据下一条代码的地址,接着执行程序,以后的程序也都是这么执行。栈区是有大小的,一般是1M左右,所以别定义太大的数组。
 

1.2.2 堆区(heap)

堆区由程序员分配内存和释放。若程序员不释放,程序结束时可能由操作系统回收。

#include <stdio.h>
char *getMem(int num)
{
	char *p1 = NULL;
	p1 = (char *)malloc(sizeof(char) * num);
	if (p1 == NULL)
	{
		return NULL;
	}
	return p1;
}
void main()
{
	char *tmp = NULL;
	tmp = getMem(10);
	if (tmp == NULL)
	{
		return ;
	}
	strcpy(tmp, "111222"); //向tmp做指向的内存空间中copy数据,注意不是向指针变量tmp中
	printf("tmp:%s\n", tmp);//输出tmp:111222
	system("pause");
	return ;
}

内存分析:

        

 

  • 堆区按内存地址由低到高方向生长,其大小由系统内存/虚拟内存上限决定,速度较慢,但自由性大,可用空间大。

堆区动态申请与释放内存
malloc(),free()等函数实现动态分布内存。

void *malloc(size_t);
  • 参数size_t是分配的字节大小;
  • 返回值是一个void*型的指针,该指针指向分配空间的首地址;
    void *型指针可以任意转换为其他类型的指针)

free()函数进行内存释放,否则会造成内存泄漏

void free(void * /*ptr*/);
  • 参数ptr是开辟的内存的首地址。
  • 无返回值;

通俗来说:

由程序员手动申请和释放
比如:int p=(int )malloc(sizeof(int)10),表示申请了一块40个字节的堆区空间,然后申请完记得用free释放。
代码区的话就是用来存放代码的,转化为二进制存放。

1.2.3 (全局)静态区

通常是用于那些在编译期间就能确定存储大小的变量的存储区,但它用于的是在整个程序运行期间都可见的全局变量静态变量
全局区有 .bss段 和 .data段组成,可读可写

#include <stdio.h>
char * getStr1()
{
	char *p1 = "abcd";
	return p1;
}
char *getStr2()
{
	char *p2 = "abcd";
	return p2;
}
 
void main()
{
	char *p1 = NULL;
	char *p2 = NULL;
	p1 = getStr1();
	p2 = getStr2();
 
	//打印p1 p2 所指向内存空间的数据,不是p1 p2中的数据
	printf("p1:%s , p2:%s \n", p1, p2);//输出p1:abcd , p2:abcd
 
	//打印p1 p2 的值
	printf("p1:%d , p2:%d \n", p1, p2);//输出p1:19184372 , p2:19184372
 
	system("pause");
	return;
}

内存分析:

         

 

  • .bss段
    未初始化的全局变量和未初始化的静态变量存放在.bss段。
    初始化为0的全局变量和初始化为0的静态变量存放在.bss段。
    .bss段不占用可执行文件空间,其内容由操作系统初始化。
  • .data段
    已初始化的全局变量存放在.data段。
    已初始化的静态变量存放在.data段。
    .data段占用可执行文件空间,其内容有程序初始化。

通俗来说:

全局区比较特殊,里面还分成了全局变量区,静态变量区,常量区。全局变量区用来存放全局变量,静态变量区用来存放带有static修饰的变量(包括静态局部变量和静态全局变量),只要含有static就存在这个区。常量区是用来存放字符常量的,还有const修饰的全局变量的,const 修饰的局部变量不存在这里,别搞混了。全局区存放的一切都是由操作系统管理,等程序结束由操作系统释放。常量区里存放的数据不可更改,就算你用指针也不行,你可能会说const修饰的局部变量都可以用指针改,但是局部变量可不是存放在常量区,这点搞清楚。

1.2.4 常量区

字符串数字等常量存放在常量区。
const修饰的全局变量存放在常量区。
程序运行期间,常量区的内容不可以被修改。

1.2.5 代码区

程序执行代码存放在代码区,其值不能修改(若修改则会出现错误)。
字符串常量define定义的常量也有可能存放在代码区。

        以上五区,代码区和全局区是在生成.exe文件之后就有了,双击.exe文件运行程序才会生成栈区和堆区。
下面直接上图:

#include<stdio.h>
#include<string.h>
int a = 10;
static int b = 20;
void fun(int x)
{
	char *p = "Hello";
	printf("形参x的地址=%d\n\n", &x);
	printf("Hello的地址=%d\n\n", "Hello");
	printf("指针变量p的地址=%d\n\n", &p);
	return;
}
int main(int argc,const char *argv[])
{
	int c = 10;
	int d = 20;
	static int e = 30;
	char *p = "Hello";
	printf("\n全局变量a的地址=%d\n\n", &a);
	printf("静态全局变量b的地址=%d\n\n", &b);
	printf("静态局部变量e的地址=%d\n\n", &e);
	printf("字符串\"Hello\"的地址=%d\n\n", "Hello");
	printf("局部变量c的地址=%d\n\n", &c);
	printf("局部变量d的地址=%d\n\n", &d);
	printf("指针变量p的地址=%d\n\n", &p);
	fun(5);
	system("pause");
	return 0;
}

 简单的用图表示了一下,总结:
全局区存放的是全局变量,静态变量,字符常量,const 修饰的全局变量。栈区存放的是局部变量和函数的形参,以及一些代码的地址,栈区的内容是可以修改的
堆区是由程序员手动申请和释放,用malloc函数申请,用free函数释放。

创作不易,如果本篇博客对您有一定的帮助,大家记得留言+点赞哦。

  • 70
    点赞
  • 163
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 44
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

去北极避暑~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值