0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

全局数据区和栈区是谁在幕后分配的?怎么分配的?

痞子衡嵌入式 来源:技术让梦想更伟大 2023-04-06 09:51 次阅读

何为变量?

变量一般可以细分为如下图:

fbfdb950-d40b-11ed-bfe3-dac502259ad0.png

本节重点为了让大家理解内存模型的“栈”,暂时不考虑“静态变量” 的情况,并约定如下:

“全局变量”仅仅默认为“普通全局变量”;

“局部变量”仅仅默认为“普通局部变量”。

如何判定全局变量和局部变量?

简单直观的来说,全局变量就是在函数外面定义的变量,局部变量就是在函数内部定义的变量,下面的例子能很清晰地说明全局变量和局部变量的判定方法:

unsigned char a;//在函数外面定义的,所以是全局变量。
voidmain()//主函数
{
 unsigned char b;//在函数内部定义的,所以是局部变量。
b=a;
while(1)
{

}
}

全局变量和局部变量的内存模型

单片机内存包括ROMRAM两部分,ROM存储的是单片机程序中的指令和一些不可更改的常量数据,而RAM存放的是可以被更改的变量数据;

也就是说,全局变量和局部变量都是存放在RAM,但是,虽然都是存放在RAM,全局变量和局部变量之间的内存模型还是有明显的区别的。

因此,分了两个不同的RAM区,全局变量占用的RAM区称为全局数据区, 局部变量占用的RAM区称为

它们的内存模型到底有什么本质的区别呢?

全局数据区就像你自己家的房间,是唯一的,一个房间的地址只能你一个人住(假设你还是单身狗的时候),而且是永久的(sorry),所以说每个全局变量都有唯一对应的 RAM 地址, 不可能重复的。

就像客栈, 一年下来每天晚上住的人不一样,每个人在里面居住的时间是有期限的,不是长久的,一个房间的地址一年下来每天可能住进不同的人,不是唯一的。

全局数据区的全局变量拥有永久产权,区的局部变量只能临时居住在宾馆客栈, 地址不是唯一的, 有期限的。

是给程序里所有函数内部的局部变量共用的,函数被调用的时候,该函数内部的每个局部变量就会被分配对应到的某个RAM地址,函数调用结束后,该局部变量就失效。

因此它对应的的RAM空间就被收回,以便给下一个被调用的函数的局部变量占用。

举例借用“宾馆客栈”来比喻局部变量所在的“栈”。

voidfunction(void);//子函数的声明

voidfunction(void)//子函数的定义
{
unsignedchara;//局部变量
a=1;
}

voidmain()//主函数
{
function();//子函数的调用
}

我们看到单片机从主函数 main 往下执行, 首先遇到function()子函数的调用, 所以就跳到function()函数的定义那里开始执行, 此时的局部变量 a 开始被分配在RAM的“栈区” 的某个地址, 相当于你入住宾馆被分配到某个房间。

单片机执行完子函数function()后,局部变量 a 在RAM的栈区所分配的地址被收回, 局部变量a 消失,被收回的RAM地址可能会被系统重新分配给其它被调用的函数的局部变量。

此时相当于你离开宾馆,从此你跟那个宾馆的房间没有啥关系, 你原来在宾馆入住的那个房间会被宾馆老板重新分配给其他的客人入住。

全局变量的作用域是永久性不受范围限制的,而局部变量的作用域就是它所在函数的内部范围。全局变量的全局数据区是永久的私人房子,局部变量的栈是临时居住的客栈。

总结如下

每定义一个新的全局变量,就意味着多开销一个新的RAM内存。而每定义一个局部变量,只要在函数内部所定义的局部变量总数不超过单片机的栈区,此时的局部变量不开销新的RAM内存, 因为局部变量是临时借用栈的, 使用后就还给栈,栈是公共区, 可以重复利用,可以服务若干个不同的函数内部的局部变量。

单片机每次进入执行函数时,局部变量都会被初始化改变,而全局变量则不会被初始化, 全局变量是一直保存之前最后一次更改的值。

有哪些常见疑问?

全局数据区栈区是谁在幕后分配的, 怎么分配的?

是C编译器自动分配的, 至于怎么分配,谁分配多一点,谁分配少一点,C 编译器会有一个默认的比例分配, 我们一般都不用管。

栈区是临时借用的,子函数被调用的时候,它内部的局部变量才会“临时” 被分配到“栈” 区的某个地址,那么问题来了,谁在幕后主持“栈区” 这些分配的工作?

单片机已经上电开始运行程序的时候,编译器已经不起作用,“栈区” 分配给函数内部局部变量的工作,确实是 C 编译器做的,但这是在单片机上电前。

C 编译器就把所有函数内部的局部变量的分配工作就规划好了,都指定了如果某个函数一旦被调用,该函数内部的哪个局部变量应该分到“栈区” 的哪个地址,C 编译器都是事先把这些“后事” 都交代完毕了才结束自己的生命。

等单片机上电开始工作的时候,虽然C编译器此时不在了,但是单片机都是严格按照C编译器交代的遗嘱开始工作和分配“栈区”的。因此,“栈区” 的“临时分配” 非真正严格意义上的“临时分配”。

函数内部所定义的局部变量总数不超过单片机的“栈” 区的 RAM 数量, 那, 万一超过了“栈” 区的 RAM数量, 后果严重吗?

这种情况专业术语叫爆栈。程序会出现莫名其妙的异常,后果特别严重。

为了避免这种情况, 一般在编写程序的时候, 函数内部都不能定义大数组的局部变量, 局部变量的数量不能定义太多太大,尤其要避免刚才所说的定义开辟大数组局部变量这种情况。

大数组的定义应该定义成全局变量,或者定义成静态的局部变量

有一些C编译器,遇到“爆栈” 的情况,会好心跟你提醒让你编译不过去,但是也有一些 C 编译器可能就不会给你提醒,所以大家以后做项目写函数的时候,要对爆栈心存敬畏。

全局变量和局部变量的优先级

刚才说到,全局变量的作用域是永久性并且不受范围限制的,而局部变量的作用域就是它所在函数的内部范围。

那么问题来了,假如局部变量和全局变量的名字重名了,此时函数内部执行的变量到底是局部变量还是全局变量?

这个问题就涉及到优先级。

注意,当面对同名的局部变量和全局变量时,函数内部执行的变量是局部变量,也就是局部变量在函数内部要比全局变量的优先级高。

我们来举一些例子

请看下面第一个例子

unsignedchara=5;//此处第1个a是全局变量

voidmain()//主函数
{
unsignedchara=2;//此处第2个a是局部变量,跟上面全局变量的第1个a重名了
print(a);//把a发送到电脑端的串口助手软件上观察
while(1)
{

}
}

正确的答案是 2。在函数内部的局部变量比全局变量的优先级更加高。

虽然这里的两个a重名了, 但是它们的内存模型不一样,第1个全局变量的a是分配在全局数据区,是具有唯一的地址的,而第2个局部变量的a是被分配在临时的栈区的,寄生在 main 函数内部。

再看下面第二个例子

voidfunction(void);//函数声明
unsignedchara=5;//此处第1个a是全局变量

voidfunction(void)//函数定义
{
 unsigned char a=3;//此处第 2 个 a 是局部变量。
}

voidmain()//主函数
{
 unsigned char a=2;//此处第 3 个 a 也是局部变量。
function();//子函数被调用
print(a);//把 a 发送到电脑端的串口助手软件上观察。
while(1)
{
}
}

正确的答案是2。因为,function这个子函数是被调用结束之后,才执行 print(a)的, 就意味函数内部的局部变量(第2个局部变量 a)是在执行 print(a)语句的时候就消亡不存在了, 所以此时print(a)的a是第3个局部变量的a(在 main 函数内部定义的局部变量的 a)。

再看下面第三个例子

voidfunction(void);//函数声明
unsignedchara=5;//此处第1个a是全局变量

voidfunction(void)//函数定义
{
unsignedchara=3;//此处第2个a是局部变量
}

voidmain()//主函数
{
function();//子函数被调用
print(a);//把a发送到电脑端的串口助手软件上观察
while(1)
{
}
}

正确的答案是5。因为function这个子函数是被调用结束之后,才执行print(a)的,就意味function函数内部的局部变量(第2个局部变量)是在执行function(a)语句的时候就消亡不存在了。

同时,因为此时main函数内部也没有定义a的局部变量,所以此时function(a)的a是必然只能是第1个全局变量的a(在main函数外面定义的全局变量的a)。






审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 单片机
    +关注

    关注

    5921

    文章

    41499

    浏览量

    614137
  • ROM
    ROM
    +关注

    关注

    4

    文章

    483

    浏览量

    84052
  • RAM
    RAM
    +关注

    关注

    7

    文章

    1170

    浏览量

    112879
  • 嵌入式编程
    +关注

    关注

    0

    文章

    22

    浏览量

    10175
收藏 人收藏

    评论

    相关推荐

    应大家要求详细讲解下C语言内存分配-通俗理解

    来实现。代码包括操作码和要操作的对象(或对象的地址引用),如果是立即数(即具体的数值,如2),将直接包含在代码中;如果是局部数据,将在
    发表于 10-08 14:13

    C语言内存分配-通俗理解

    来实现。代码包括操作码和要操作的对象(或对象的地址引用),如果是立即数(即具体的数值,如2),将直接包含在代码中;如果是局部数据,将在
    发表于 10-08 14:57

    keilC51编译器在内存分配时知道避开模拟吗?

    放在1K的位置,会不会出问题?编译器在内存分配的时候知不知道避开模拟呢?
    发表于 04-08 09:34

    全局变量数组分配分太多怎么办

    全局数组分配的太多太大会不会 跟 子函数里的数组有交集现象啊,我知道我的问题很白痴,一个在全局数据段 一个在里,位置都不一样,但是 通过运行后的数据分析,好像真的看到了有重叠的部分,现象把我
    发表于 05-29 04:35

    全局数组和全局变量之类的数据结构会对RTOS带来什么样的影响?

    移植到FreeRTOS系统上。之前的协议是基于前后台系统开发的,在串口接收中断将接收到的数据写入一个环形缓存,再根据协议从这个缓存中读出一帧数据,将数据转义和解析,将最后解析
    发表于 06-13 09:00

    freertos与STM32如何分配堆栈空间

    、堆、全局、常量、代码、RAM、ROM,及如何分配堆栈空间基于STM32分析、堆、全局、常量、代码、RAM、ROM FreeRTOS任务大小确
    发表于 08-03 06:36

    stm32的代码和常量的地址分配在哪

    和常量的地址分配在哪?stm32的全局变量和堆栈的地址又分配在哪?
    发表于 12-02 06:05

    如何对RAM空间分配操作

    分配,包括对内存RAM的空间分配,对RAM空间分配操作,可以理解如下:分配全局变量区分配的大小在编译器或者配置文件中定义,用于存放函数调用
    发表于 01-20 08:05

    谈一谈单片机程序的与堆

    分配由c/C++编译的程序占用的内存分为以下几个部分1、(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的。2、堆(heap
    发表于 02-28 07:35

    static的全局变量与局部变量的使用,看完你就懂了

    全局数据的静态变量,包括全局静态变量和局部静态变量,都在全局数据分配内存。初始化的时候自动初始化为 0。 (4)不想被释放的时候
    发表于 06-27 08:54

    对于51单片机的RAM内存分配(包含分配

    空间(一字节的空间是在启动文件里暂时分配的)程序里定义的全局变量会放在静态存储,局部变量、形参、嵌套函数地址和字符串首地址会放在,字符串会放在ROM,00h-1Fh 32个字节是4组常规寄存器,通过 PSW中的RS
    发表于 11-20 12:51 10次下载
    对于51单片机的RAM内存<b>分配</b>(包含<b>栈</b>的<b>分配</b>)

    内存分配的三种情况解析:静态、、堆

    : 主要存放static静态变量、全局变量、常量 。这些数据内存在编译的时候就已经为他们分配好了内存,生命周期是整个程序从运行到结束。 (2): 存放局部变量 。在执行函数的时候
    发表于 12-15 11:26 1715次阅读

    C语言堆栈程序内存的分配

    。 堆(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。 全局(静态)(static)—,全局变量和静态变量的存储是放在一块的,初始
    的头像 发表于 10-21 14:51 1912次阅读

    C语言怎么建立内存的动态分配

    全局变量是分配在内存中的静态存储的,非静态的局部变量,包括形参是分配在内存中的动态存储的,这个存储是一个“”的区域。
    的头像 发表于 03-10 15:30 382次阅读

    全局数据包通信简介

    全局数据包通信简介 对于PLC之间的数据交换,只关心数据的发送和接收全局数据包的通信方式是在配PLC硬件的过程中,组态所要通信PLC站之间的发送和接收,不需要任何程序处理,这种通信方式
    的头像 发表于 05-29 10:04 238次阅读
    <b>全局数据</b>包通信简介