当前位置:首页 > 公众号精选 > 嵌入式云IOT技术圈
[导读]本文介绍了数据段、代码段、堆栈段、BSS段的区别。


微信公众号:morixinguan
关注可了解更多的教程。问题或建议,请公众号留言;
如果你觉得本文对你有帮助,欢迎赞赏

▲长按图片保存可分享至朋友圈


一、数据段、代码段、堆栈段、BSS段的区别

进程(执行的程序)会占用一定数量的内存,它或是用来存放从磁盘载入的程序代码,或是存放取自用户输入的数据等等。不过进程对这些内存的管理方式因内存用途不一而不尽相同,有些内存是事先静态分配和统一回收的,而有些却是按需要动态分配和回收的。对任何一个普通进程来讲,它都会涉及到5种不同的数据段下面我们来简单归纳一下进程对应的内存空间中所包含的5种不同的数据区都是干什么的。

BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。

数据段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。

代码段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)

栈(stack):栈又称堆栈,是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进后出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

是由操作系统分配的,内存的申请与回收都由OS管理。

全局的未初始化变量存在于.bss段中,具体体现为一个占位符;全局的已初始化变量存于.data段中;而函数内的自动变量都在栈上分配空间。.bss是不占用.exe文件空间的,其内容由操作系统初始化(清零);而.data却需要占用,其内容由程序初始化,因此造成了上述情况。

bss段(未手动初始化的数据)并不给该段的数据分配空间,只是记录数据所需空间的大小。data(已手动初始化的数据)段则为数据分配空间,数据保存在目标文件中。数据段包含经过初始化的全局变量以及它们的值。BSS段的大小从可执行文件中得到,然后链接器得到这个大小的内存块,紧跟在数据段后面。当这个内存区进入程序的地址空间后全部清零。包含数据段和BSS段的整个区段此时通常称为数据区。

接下来,我们来看一个例程,将告诉你,程序中上面所说的究竟在什么位置,请看代码:

 1#include   2#include   3//位于BSS段,存放在程序组未初始化的内存区域  4int BSS ;  5//位于数据段,存放在程序中已经初始化的内存区域  6int data = 100 ;  7//静态区  8static int y ;  9int stack(void) ; 10 11int main(void) 12{ 13 //静态区 14 static int k  ; 15//栈区内存自动申请自动释放 16int i , j ; 17 int *p = NULL ; 18//堆区内存手动申请手动释放 19 p = malloc(1024) ; 20 free(p); 21 return 0 ; 22} 23int stack(void) 24{ 25 //栈区 26 int i ; 27 return 0 ; 28}

那么,这些段最终又是怎么被加载的呢?

我们以ARM嵌入式Linux系统为例,嵌入式系统在编译链接的过程中会通过一个叫链接脚本的东西,告诉链接器,输入的程序文件中的各个段放到输出的文件中区,然后控制各个段在内存中的布局,这样程序在运行时就有地址空间布局了。

我们来看一个简单的连接脚本:test.lds

 1//指定程序的输出格式  2//大端、小端  3OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm",  4 "elf32-littlearm")  5//输出对应平台的体系结构  6OUTPUT_ARCH(arm)  7//将SYMBOL的值设置成入口地址,这里一般都是_start  8ENTRY(_start)  9//指定共享库 10SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); 11SECTIONS 12{ 13 //表示从0x40008000这个地址开始加载各个段 14 . = 0x40008000 ; 15 //文本段 16 .text : 17 { 18 test.o(.text); 19 *.o(.text); 20 } 21 //只读数据段 22 .rodata:{*(.rodata)} 23 //设置对齐格式为4个字节 24 align = 4 ; 25}

二、位,字,字节,字符等基础

(1)字(Word):在ARM体系结构(32位机)中,字的长度是32位,而在8位/16位处理器体系结构中,字的长度是16位长是衡量计算机性能的一个重要的技术指标。

(2)半字(Half-Word):在ARM体系结构(32位机)中,半字的长度是16位,而在8位/16位处理器体系结构中,半字的长度与字的长度一致。

(3)字节(Byte):在ARM体系结构(32位机)和8位/16位处理器体系结构中,字节的长度均是8位。字节一般用于表示存储容量,例如

1KB = 2^10B = 1024 B 1MB = 2^10KB = 1024KB = 2^20B

1GB = 2^10MB=1024MB=2^30B   1TB=2^10GB=1024GB=2^40B

(4)位(bit) :一个字节等于8bit , 1位的二进制的数码一般用0或1来表示。

(5)汉字:一个汉字由两个字节组成,如“你”,在其它编码情况下,汉字有可能也由三个字节组成。

(6)字母:一个字母由一个字节组成,如“V”

(7)字符:字符指一个字母或一个字或一个标点或一个符号,不一定几个字节,看情况定。

三、进制转换基础

(1)十进制

我们在日常生活中,只要是和钱相关的东西,我们都在使用十进制,包括我们在学习数学课程的时候,经常用到的也是十进制。十进制也就是基数为10,逢10进1。在十进制中,一共使用10个不同的数字符号,这些符号处于不同位置时,其权值各不相同。

(2)二进制 基数为2,逢2进1。在二进制中,使用0和1两种符号。

比如:

1+1=10(这里的10是二进制的2) 1+0=1  11+1=100(这里的100是二进制的4)

如何算的呢?

如下:

(3)十六进制 基数为16,逢16进1。十六进制使用16种不同的符号,它们与二进制的转换关系为: 0:0000 1:0001 2:0010 3:0011 4:0100 5:0101 6:0110 7:0111 8:1000 9:1001 A:1010 B:1011 C:1100 D:1101 E:1110 F:1111

以上的计算,我们可以使用Window自带的计算机来进行验证,如何使用?

在程序----->附件中找到计算器,如图3-1-1所示,这个就是我们电脑附件自带的计算器,的功能很强大。

图3-1-1 window系统自带的计算器

点击查看,选择程序员,如图3-1-2,我们就可以使用这个便捷的工具来计算了。

图3-1-2 选择程序员专用的计算器

(4)接下来,我们来看一个例子,如何将字符串中的十进制数转化为十六进制数,或者将十六进制数转化为十进制数的算法。

 1#include   2typedef char TUINT8 ;  3typedef int TUINT32;  4TUINT32 Read_DecNumber(const TUINT8* str);  5TUINT32 Read_HexNumber(const TUINT8* str);  6int main(void)  7{  8 int ret = Read_DecNumber("1000");  9 int d = Read_HexNumber("A"); 10 printf("将字符串中的数字转化为10进制数 :%d\n",ret); 11 printf("将字符串中的16进制数转化为10进制数 :%d\n",d); 12 return 0 ; 13} 14//将字符串中的数字转化为10进制数 15TUINT32 Read_DecNumber(const TUINT8* str){ 16 TUINT32 value; 17 if (! str) 18 return 0; 19 value = 0; 20 while ((*str >= '0') && (*str <= '9')){ 21 value = value*10 + (*str - '0'); 22 str++; 23 } 24 return value; 25} 26//将字符串中的16进制数转化为10进制数 27TUINT32 Read_HexNumber(const TUINT8* str) 28{ 29 TUINT32 value; 30 if (! str) 31 return 0; 32 value = 0; 33 while (1) 34{ 35 if ((*str >= '0') && (*str <= '9')) 36 value = value*16 + (*str - '0'); 37 else if ((*str >= 'A') && (*str <= 'F')) 38 value = value*16 + (*str - 'A') + 10; 39 else if ((*str >= 'a') && (*str <= 'f')) 40 value = value*16 + (*str - 'a') + 10; 41 else 42 break; 43str++; 44 } 45 return value; 46}

编译运行结果,如图3-1-3所示。

图3-1-3 10进制数与16进制数互转的结果

当然,嵌入式软件的开发基础远不止上述内容,但最最基础的也往往逃不过这些内容。



免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

如今,汽车已经不再是一个简单的交通工具,而是一种智能化的移动终端。通过集成各种先进的传感器、控制器和执行器,汽车可以实现对环境的感知、分析和决策,实现自动驾驶、智能导航、车辆间通信、互联网连接等功能。这些功能不仅提高了驾...

关键字: HMI 驾驶舱 嵌入式软件

为增进大家对嵌入式的认识,本文将对嵌入式软件方向的分类以及嵌入式的应用予以介绍。

关键字: 嵌入式 指数 嵌入式软件

在这篇文章中,小编将为大家带来嵌入式软件及其作用的相关报道。如果你对本文即将要讲解的内容存在一定兴趣,不妨继续往下阅读哦。

关键字: 嵌入式 嵌入式软件

嵌入式软件测试是针对嵌入式系统中的软件进行验证和验证的过程。嵌入式系统是集成在其他设备或系统中的计算机系统,例如汽车、医疗设备、家电等。为确保嵌入式软件的质量和可靠性,进行适当的测试是至关重要的。嵌入式软件测试是确保嵌入...

关键字: 嵌入式软件 计算机 硬件

嵌入式软件开发已经成为现代科技领域中不可或缺的一部分。从智能手机到家用电器,从汽车到医疗设备,嵌入式软件无处不在。然而,嵌入式软件的开发具有挑战性,因为它需要满足高度的性能、稳定性和可靠性要求。为了确保嵌入式软件的成功开...

关键字: 嵌入式软件 智能手机 家用电器

在嵌入式软件开发中,利用完整的应用跟踪,可为开发人员分析其产品行为提供无限的可能性。通过对应用程序的全面了解,他们可以跟踪每一条指令,看看他们的应用程序是否按照预期运行,或者是否出现错误或漏洞。那么,如何才能最大化地利用...

关键字: RISC-V 嵌入式软件

2023年上半年收入7.459亿元 同比增长5.1% 毛利率水平上升 海外收入同比增长65.4% 香港2023年8月22日 /美通社/ -- 金邦达宝嘉控股有限公司及其附属公司(以下合称「金邦达」、「...

关键字: 数字化 代码 嵌入式软件 COM

【2023 年 6 月 9 日,德国慕尼黑讯】英飞凌科技股份公司(FSE代码:IFX / OTCQX代码:IFNNY)通过在现有的AUTOSARv4.2.2 MCAL基础上增加对AUTOSARv4.4.0的支持,进一步扩...

关键字: 嵌入式软件 发动机

2022年上半年收入7.1亿元 同比增长14.5%  嵌入式软件和安全支付产品发展势头强劲 收入同比增长28.7% 香港2022年8月16日 /美通社/ -- 金邦达宝嘉控股有限公司及其附属公司(以下合称“...

关键字: 数字化 嵌入式软件 供应链 代码

文|周立功日期|2021年12月2日ZLG致远电子创始人周立功EsDA(EmbededsoftwareDesignAutomation),全称是嵌入式软件设计自动化,它是一个由多种软件组成的软件过程管理和开发的工具,致力...

关键字: 自动化 嵌入式软件 ESD 周立功
关闭
关闭