.text .data .bss .stack .heap 详解

42 篇文章 4 订阅
10 篇文章 0 订阅

.text 代码段:用来存放代码和常量(const 关键字定义的变量)。

.data 数据段:用来存放有初始值的全局变量、全部静态变量(static 关键字定义的变量)。注意全部静态变量包括全局静态变量局部静态变量,并且不论这些变量是否有初始值。即不管有没有初始值,也不管是全局变量还是定义在函数内的局部变量,只要是用 static 关键字定义的变量,都放在 .data 数据段内。

.bss BSS:用来存放没有初始值的全局变量。(没有初始值的局部变量好像也存在这里,网上验证帖子,待验证)。

.stack 栈区: 用来存放局部变量,函数的参数,返回值等,由编译器自动分配释放如一个函数被调用后,产生的临时变量都会存到栈区的顶部,当函数完成后,会自动从顶部将刚使用的数据销毁。栈区的地址是从高地址向下增长的

.heap 堆区:  用来动态内存分配,如 malloc, new 申请的内存,由程序员手动分配释放程序中不释放,则程序结束时,OS回收;据说这个和数据结构中的堆 没有什么关系;堆区使用时地址向上增长。

 

以上5段,在生成的 hex 文件中是怎么分配呢?

Hex 文件分为三部分(可通过 map 文件查看到)

  1. .text 代码段

  1. .data 数据段

  1. .bss, .stack, .heap的位置信息(即起始位置和大小)

所以 hex 文件中的数据包括:代码、常量或初始值、三段(.bss, .stack, heap)的位置信息等。

 

程序运行时,这5段各起什么作用,又在物理存储空间哪个位置?

现在的计算机架构主要有两种:

  1. 冯诺依曼架构:必须从 RAM 中取出执行代码,即执行代码要先存入 RAM 中。
  2. 哈佛架构:可以直接从 flash 执行代码。

ARM 架构采用的哈佛改善架构。(此链接的下图位置有简单介绍)

如上所述, ARM 架构是哈佛架构,可以直接从 flash 取指令执行,所以在程序运行期间

.text 位于 flash ,具体位置和 hex 文件中 .text 段的位置一致,程序运行时直接从 flash 内读取指令并执行。.text 段都是代码和常量,只读,所以可以放在 flash 内。

 .data MCU启动过程中,会被从 flash copy SRAM (各家的启动代码都会做此操作)。程序运行时是操作 SRAM 中的变量。 .data 段内都是变量,可读可写,所以会被放在 SRAM 内。所以 .data 段有两个地址,一个是在 flash 内的地址,flash 内放的就是变量的初始值;另一个是在 SRAM 内的地址,SRAM 内的地址就是程序运行时变量所在的位置。一个变量初始化的过程,就是启动代码将 .data 段从 flash copy SRAM 中的过程,因此 SRAM 中的 .data 区域,在程序运行时(MCU 启动代码运行之后)一开始就有值了。这也解释了,为什么 static 变量的初始化只执行一次(即在编译时初始化),注意这种通过启动代码 copy flash 中内容到对应 SRAM 中的所谓在编译时初始化,和函数内部局部变量是通过用户代码初始化是不一样的操作,局部变量通过用户代码初始化后面会详细讲解。另外要注意,.data 段内数据分有初始值的全局变量和 static 变量两块,这两块变量虽然都在 .data 段,但作用域不同,全局变量是全局作用域,而 static 变量是局部作用域

.bss, .stack .heap 在 hex 文件内有对应的起始位置和大小信息,MCU 在启动时(启动代码)会根据这些信息配置 SRAM,给这三段在 SRAM 中按 hex 文件中的信息分配好对应的位置和大小。程序运行期间,对这三段的操作都是在 SRAM 中进行的。注意 .bss 要初始化,一般是启动代码会做这个操作,将整个 .bss 段初始化为 0 (可参考bss段为什么要初始化)。

 

总结一下这5段在物理存储器上的位置及占用大小:

Flash 占用大小 =  .text 大小 + .data大小 + 其它section(如.bss, .stack, .heap等) 位置信息大小

SRAM 占用大小 = .data 大小 + .bss 大小 + .stack大小 + .heap大小

 

程序运行时,这 5 段在物理存储器上的位置如下图

 

 

下面用一段代码来详细介绍上面这5

 

各变量所在位置如上图注释,下面把一些重点概念,如函数中的局部变量如何赋值,用此代码讲解一下:

在编译时初始化和函数内局部变量用户代码初始化

如下图

全局变量 a 和 静态变量 e 是在编译时初始化:这两个变量都在 .data 段,MCU 启动时,启动代码会将 .data 段从 flash copy 到 SRAM ,copy 操作完成后, SRAM  .data 段中的变量就都已经有了初始值(之前存在 flash 中的初始值)。本例中的变量 a 和 c 就是在这个过程中完成的初始化。这个初始化过程中,不需要调用任何用户代码,所以被称作在编译时初始化注意:编译时初始化只初始化一次。因为是启动代码执行 copy 操作完成的初始化,启动代码只执行一次,所以这个初始化的操作也就只会执行一次。

而局部变量 g 在 .stack 中,也就是说,只有在程序运行时,并在调用 main 函数时,才会在 SRAM 的 stack 区域内给变量 g 开出一个空间,让程序可以对 g 进行操作。那如何对变量 g 赋初始值 2 的呢?是执行 g = 2; 这段代码,g =2; 这段代码在 .text 中(存在 flash 中),每次调用 fun 函数时都会调用 g = 2; 这段代码,这段代码会给在 stack 中的变量 g 赋初始值 2。但和编译时初始化不同,每次调用 fun 函数,都会重新对变量 g 做赋值操作,即函数被调用几次,函数内局部变量的初始化操作就会执行几次。所以用户代码初始化函数内局部变量会初始化多次

 

函数内局部变量初始化和函数内静态变量初始化的区别

局部变量 g 的初始化上面已详细讲解。

局部静态变量 y 的初始化和 g 不同,静态变量 y 是在 .data 段中,和全局变量 a 一样,是用在编译时初始化的方法完成初始化的。详细过程参考上面关于变量 a 的初始化过程。

注意:这样 g y 在初始化值方面的用法就完全不同了。在 fun 函数每次被调用时, g 都被初始化为2,而 y 则不是一直是 3,y 的值是上次 fun 被调用时 y 被赋的值(类似静态变量 y 使用的实例参考本链接)。

用 g 和 y 在 SRAM 中的位置也可以解释它们的不同:g 处于 SRAM 的 stack 区域,只有 fun 函数被调用时才给 g 分配空间,并且每次分配后,都会对 g 赋初始值 2,所以每次调用 fun 函数,g 初始值都是2,并且函数 fun 执行完后下一句指令(pull)会自动注销 g 在 stack 中的空间;而 y 处于 SRAM 的 .data 区域,y 在 SRAM中有一个固定的物理空间,即使 fun 函数执行完毕,y 在 SRAM 中还是有一个空间保留着 y 的值,static int y = 3; 这句其实和定义全局变量初始值一样,只是在编译时将 y 放在了 .data段,并在 flash 中保存了其初始值 3, 这样在 MCU 启动时完成了对 y 的初始化,y 也就只在这时等于 3,之后 fun 执行中只要改变了 y 的值,下次再调用 fun 时,y 中还是上次的值,y=3; 这句代码完全不会执行。

static 变量详细介绍和使用可查看本链接。

 

对于常量

c 位置在 .text 段,程序运行期间 c 是在 flash 内,即程序要用到 c 时会去 flash 的 c 地址处直接取值。和全局变量 a 不同,启动代码不会 copy c 到 SRAM 中,SRAM 中不会有 c 的位置。

 

 

 

Code, RO, RW, ZI  

这些只是用 keil 编译器时,对 .text, .data, .bss, .statck, .heap 这 5 段的不同称呼,详细介绍可参考本链接。简单总结如下:

Code:代码的大小

RO:常量所占空间

RW:程序中已经初始化的变量所占空间

ZI:未初始化的static和全局变量以及堆栈所占的空间

 

Flash占用大小=Code+RO+RW

SRAM占用大小=RW+ZI

  • 15
    点赞
  • 106
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
.bss是指程序中未初始化的全局变量和静态变量所占用的内存空间。这部分内存在程序加载时会被清零,因此不需要额外的初始化操作。 stack是指函数调用和局部变量所使用的内存空间。每当一个函数被调用时,会在栈上分配一块内存空间来存储函数的参数、局部变量以及函数的返回地址等信息。当函数调用结束时,这块内存空间会被释放。 它们在内存中的位置和分配方式是不同的。.bss位于程序的数据段,而stack位于栈段。.bss的大小是在编译时确定的,而stack的大小是根据函数调用的深度和局部变量的需求动态分配的。 因此,.bssstack是两个不同的内存区域,用于存储不同类型的数据。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [bssdatatextheap(堆)与stack(栈)](https://blog.csdn.net/baicong9439/article/details/101089290)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [.text .data .bss .stack .heap 详解](https://blog.csdn.net/phenixyf/article/details/116718762)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值