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

这篇具有很好参考价值的文章主要介绍了【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修饰的变量 可读可写
常量区 用于初始化变量的常量 只读
代码区 代码指令 只读

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

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;
}

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

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

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

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

到了这里,关于【C语言】-- 一篇带你了解C语言内存五大区——栈区,堆区,全局区,常量区,代码区的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构】一篇带你彻底了解栈

    栈:一种线性数据结构,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶 (Top), 另一端称为栈底 [Bottom]。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。即最后进入的元素最先被访问。 压栈:栈的插入操作叫做进栈/压栈

    2024年02月05日
    浏览(43)
  • [Linux 基础] 一篇带你了解linux权限问题

    Linux下有两种用户:超级用户(root)、普通用户。 超级用户:可以再linux系统下做任何事情,不受限制 普通用户:在linux下做有限的事情。 超级用户的命令提示符是“#”,普通用户的命令提示符是“ $ ” 命令: su [用户名] 功能: 切换用户。 例如,要从root用户切换到普通用

    2024年02月08日
    浏览(19)
  • [ C++ ] 一篇带你了解C++中隐藏的this指针

    本篇文章我们将一起讨论在有趣的知识点--隐藏的this指针。本篇我们要使用到之前我们所学习到的C++类与对象(1),如果有各位小伙伴还不曾了解类与对象的简单思想,可以访问上篇博客:[ C++ ] 带你一篇了解什么是OOP(面向对象编程),什么是封装? -- 类与对象(上) 目录 1.

    2024年02月07日
    浏览(27)
  • [数据结构 -- 手撕排序第二篇] 一篇带你详细了解希尔排序

    目录 1、常见排序算法 1.1 插入排序基本思想 2、希尔排序 2.1 希尔排序( 缩小增量排序 ) 2.1.1 预排序阶段 2.1.2 插入排序阶段 2.2 单趟希尔排序 2.2.1 思路分析 2.2.2 代码实现 3、希尔排序代码实现 4、希尔排序时间复杂度 5、希尔排序与插入排序效率对比 6、希尔排序特性总结 直接

    2024年02月13日
    浏览(22)
  • [C++]类与对象(下) -- 初始化列表 -- static成员 -- 友元 -- 内部类,一篇带你深度了解。

      目录 1、再谈构造函数 1.1 构造函数体赋值 1.2 初始化列表 1.2.1 初始化列表的意义 1.3 explicit 2、static成员 2.1 问题引入 2.2 特性 3、友元 3.1 友元函数 3.2 友元类 4、内部类 在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。 我们构造函

    2024年02月12日
    浏览(22)
  • 数组(一篇带你掌握数组)

        在之前,我们想要存储一个数据的时候可以创建变量,例如存储一个整形的变量,我们使用int类型的变量来存储,那么如果存储一组相同类型的数据呢?这时我们就引入了 数组 的概念。 目录 一、一维数组的创建和初始化 1.1数组的创建 1.2 数组的初始化 1.3 一维数组的使

    2023年04月08日
    浏览(24)
  • 一篇带你精通MPLS

    MPLS:多协议标签交换 可以基于多种不同的3层协议来生成2.5层的标签信息 包为网络层的PDU,故包交换就是基于IP地址进行数据转发;也就是路由器的路由行为。(路由器和终端基于3层的IP地址数据转发的路由行为) 原始包交换 查两张表 在包交换过程中,数据包每经过一个路

    2024年02月22日
    浏览(25)
  • Javaの一篇带你吃透接口

    随着接口的到来,JavaSE的学习笔记大结局也即将来临,最近的几篇博客写到了封装,继承,多态,抽象类等等,都循序渐进得介绍了这类的知识,大家如果接口这一块理解的很困难的话,建议去完善一下前面的知识哦 👉Java封装 👉静态成员 👉代码块 👉内部类 👉继承 👉多

    2023年04月08日
    浏览(26)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包