赞
踩
一、 在c中分为这几个存储区
内存区域 | 存放内容(elf存放区域) | 释放时间 |
---|---|---|
栈 | 在函数体中定义的变量通常是在栈上(栈) | 由编译器自动分配释放 |
堆 | 用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上(堆) | 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收 |
全局区(静态区) | 全局变量和静态变量的存储是放在一块的。初始化的全局变量和静态变量在一块区域(data段),未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(bss段)。 | 程序结束释放 |
常量区域 | 函数中的"adgfdf"这样的字符串存放在常量区(text段) | 程序结束释放 |
程序代码区 | 存放二进制代码(text段) | / |
注意:在所有函数体外定义的是全局量,加了static修饰符后不管在哪里都存放在全局区(静态区),在所有函数体外定义的static变量表示在该文件中有效,不能extern到别的文件用,在函数体内定义的static表示只在该函数体内有效。
int a = 0; //全局初始化区
char *p1; //全局未初始化区
void main() {
int b; //栈
char s[] = "abc"; //栈
char *p2; //栈
char *p3 = "123456"; / /123456{post.content}在常量区,p3在栈上
static int c = 0; //全局(静态)初始化区
p1 = (char *)malloc(10); //分配得来得10字节的区域在堆区
p2 = (char *)malloc(20); //分配得来得20字节的区域在堆区
strcpy(p1, "123456"); //123456{post.content}放在常量区,编译器可能会将它与p3所指向的"123456"优化成一块
}
堆栈基本对比
堆 | 栈 |
---|---|
是系统提供的功能,特点是快速高效,缺点是有限制,数据不灵活 | 是函数库提供的功能,特点是灵活方便,数据适应面广泛,但是效率有一定降低 |
栈是系统数据结构,对于进程/线程是唯一的 | 堆是函数库内部数据结构,不一定唯一;不同堆分配的内存无法互相操作; |
栈空间分静态分配和动态分配两种。静态分配是编译器完成的,比如自动变量(auto)的分配。动态分配由alloca函数完成。栈的动态分配无需释放(是自动的),也就没有释放函数。为可移植的程序起见,栈的动态分配操作是不被鼓励的 | 堆空间的分配总是动态的,虽然程序结束时所有的数据空间都会被释放回系统,但是精确的申请内存 |
、 | 堆 | 栈 |
---|---|---|
碎片问题 | 对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低 | 对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出 |
生长方向 | 生长方向是向上的,也就是向着内存地址增加的方向 | 生长方向是向下的,是向着内存地址减小的方向增长 |
分配方式 | 堆都是动态分配的,没有静态分配的堆 | 栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现 |
分配效率 | 栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高 | 堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多 |
bss段(bss segment) 用于存放程序中 未初始化的全局变量和静态局部变量 。在目标文件中,这个段并不占据实际空间,它仅是个占位符。
bss段属于 静态内存分配 。
数据段(data segment) 通常指存放程序中 已始化的全局变量和静态局部变量 的一块内存区域,读写属性 。
数据段属于 静态内存分配。
代码段(code segment/text segment) 通常指存放程序 执行代码的一块内存区域 ,也可能包含些只读的 常数变量(如字符串常量等),只读属性。
这部分区域的大小在程序运行前就已确定,且内存区域通常属于只读(某些架构也允许代码段为可写,即允许修改程序)。
堆是用于存放进程运行中被动态分配的内存段,大小不固定,可动态扩张或缩减。
当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);
当用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。
栈又称堆栈,是用户存放程序临时创建的局部变量,
是函数括弧“{}”中定义的变量(不包括static声明的变量,static意味着在数据段中存放变量)。
此外,在函数被调用时,其参数也会被压入发起调用的进程栈中,且待到调用结束后,函数的返回值也会被存放回栈中。
由于栈的先进先出(FIFO)特点,所以栈特别方便用来保存/恢复调用现场。
从这个意义上讲,可把堆栈看成一个寄存、交换临时数据的内存区。
## 解剖 Linux C 程序.
file #查看文件属性
xxd #二进制格式文件转hex格式 例: xxd t1.o > t1.hex
#ELF 文件基本介绍
readelf
objdump
# 如何精简 Linux C 小程序
stdlibc #介绍 (gnulibc), 如何去掉链接stdlibc
注:macOs下无这两个命令,可用brew安装,brew update && brew install binutils
,然后用greadelf和gobjdump。
这样的概念,不知道最初来源于哪里的规定,但在当前的计算机程序设计中是很重要的一个基本概念。
且在嵌入式系统的设计中也非常重要,牵涉到嵌入式系统运行时的内存大小分配,存储单元占用空间大小的问题。
在采用段式内存管理的架构中(比如intel的80x86系统),bss段通常指用来存放程序中未初始化的全局变量的一块内存区域,
一般在初始化时bss 段部分将会清零。bss段属于静态内存分配,即程序一开始就将其清零了。
如,在C语言之类的程序编译完成后,已初始化的全局变量存在.data 段中,未初始化的全局变量保存在.bss 段中。
text和data段都在可执行文件中(在嵌入式系统里一般是固化在镜像文件中),由系统从可执行文件中加载;
而bss段不在可执行文件中,由系统初始化。
全局的未初始化变量存在于.bss段中,具体体现为一个占位符;
全局的已初始化变量存于.data段中;
而函数内的自动变量都在栈上分配空间;
.bss是不占用文件空间的,其内容由操作系统初始化(清零);
.data却需要占用,其内容由程序初始化。因此造成了上述情况。
bss段(未手动初始化的数据)并不给该段的数据分配空间,只是记录数据所需空间的大小;
bss段的大小从可执行文件中得到 ,然后链接器得到这个大小的内存块,紧跟在数据段后面。
data段(已手动初始化的数据)则为数据分配空间,数据保存在目标文件中;
data段包含经过初始化的全局变量以及它们的值。当这个内存区进入程序的地址空间后全部清零。
包含data段和bss段的整个区段此时通常称为数据区。
// t1.c
int a = 1;
static int b = 4;
int c;
static int d;
char *s1 = "1234";
int main() {
int e = 2;
char *s2 = "3456";
return 0;
}
编译调试文件
gcc test1.c -o test1.o //编译
objdump -sx t1.o //调试
摘取相关信息如下:
Sections:
Idx Name Size VMA LMA File off Algn
……
15 .rodata 0000001a 0000000000400570 0000000000400570 00000570 23
CONTENTS, ALLOC, LOAD, READONLY, DATA
……
24 .data 00000018 0000000000601020 0000000000601020 00001020 23
CONTENTS, ALLOC, LOAD, DATA
25 .bss 00000010 0000000000601038 0000000000601038 00001038 2**2
ALLOC
Sections展示了不同的段的大小。
SYMBOL TABLE:
000000000060102c l O .data 0000000000000004 b
000000000060103c l O .bss 0000000000000004 d
……
0000000000601030 g O .data 0000000000000008 s1
0000000000601040 g O .bss 0000000000000004 c
0000000000601028 g O .data 0000000000000004 a
SYMBOL TABLE展示了不同的变量是存在哪的。这里就可以看到
data段有:
- 初始化了的全局变量a
- 初始化了的静态变量b
- 常量字符串s1
bss段有:
- 未初始化的全局变量c
- 未初始化的静态变量d
Contents of section .rodata:
400550 01000200 31323334 00333435 3600 ....1234.3456.
Contents of section .data:
6008a8 00000000 00000000 00000000 00000000 ................
6008b8 01000000 04000000 54054000 00000000 ........T.@.....
可以看到rodata段中有我们定义的1234和3456
而data段里有1和4(小端)分别对应a和b
还可以读取elf文件,命令是readelf
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。