程序的内存分配----变量在可执行文件中的内存区分配
一、 预备知识—程序的内存分配
一个由c/C++编译的程序占用的内存分为以下几个部分:
- 程序代码区(.text) — 存放函数体的二进制代码
- 文字常量区(.rodata) — 常量字符串就是放在这里的, 程序结束后由系统释放。
- 全局区(静态区)(static)— 全局变量 和 静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域(.data),未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(.bss)。 - 程序结束后由系统释放。
- 堆区(heap)— 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意: 它与数据结构中的堆是两回事,分配方式倒 是类似于链表。
- 栈区(stack)— 由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
二、 例子程序
//main.cpp
int a = 0; // 全局初始化区
char *p1; // 全局未初始化区
main()
{
int b; // 栈区
char s[] = "abc"; // 栈区
char *p2; // 栈区
char *p3 = "123456"; // p3在栈区; "123456\0" 在常量区,
static int c =0; // 全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20); // 分配得来的10和20字节的区域就在堆区
strcpy(p1, "123456"); // "123456\0" 放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}
=================================================================================
C语言变量分配的实例分析:
在可执行文件中,变量会被分配在哪些区里?这里以可执行文件为例子,可执行文件有固定的内存加载地址,符号(函数/变量的名字)将来在内存里的地址连接器是可以提前确定的。
源程序编译连接的结果是形成一堆汇编指令代码,大致分为.text .data .bss等几个section。
对于.exe文件和.so文件,
全局和静态变量都放在.data 或.bss段 (gas把源文件从头到尾扫描1遍,才知道一个变量的全部情况:是否定义;类型;是否初始化。然后把初始化的变量在.data段里分配位置和空间,把没初始化的变量在.bss段里分配位置和空间,没定义的变量分配在.undef段)。
汇编指令代码里全局变量表现为一个内存地址(全局变量在目标文件里是一个偏移值,加载进内存里是一个内存地址)。临时变量在汇编代码里变成ebp/esp+n,表现为一个堆栈地址,化为程序正文(.text)的一部分。有些变量的最终内存地址在加载进内存之前还不能确定,需要加载进内存才可以计算出来.
全局变量 作用域是跨越多个源程序的。因此全局变量不能重名。静态变量作用域是位于单个源程序内。多个源程序可以有同名的全局静态变量。本例中,为了区分多个同名的静态变量,gcc 用 c444和c444.0 来加以区别。
[test@redhat]# more aaa.c
# include <stdio.h>
int a111 = 0; # 全局变量 已初始化
char *p111 = "654321"; # 全局指针变量 已经初始化
static int c444 = 9; # 静态全局变量 已经初始化
static int c555; # 静态全局变量 未初始化
main()
{
int b222; # 局部变量
char s333[] = "abc"; # 局部变量
char *p222; # 局部变量
char *p333 = "123456"; # 局部变量
static int c444 =0; # 已初始化静态局部变量,与前面静态全局变量重名
p111 = (char *)malloc(10);
p222 = (char *)malloc(20);
strcpy(p111, "123456");
}
----------------------------------------------------------------------------------------------------------------
文件./aaa加载进内存后,再看看变量的地址以及所在的区: (其中的符号,变量,函数 见下面列表)
[test@redhat]# gdb ./aaa
GNU gdb 6.1.1
(gdb) disassemble main
Dump of assembler code for function main:
0x0804838c <main+0> : push %ebp
0x0804838d <main+1>: mov %esp,%ebp
0x0804838f <main+3> : sub $0x18,%esp
0x08048392 <main+6>: and $0xfffffff0,%esp
0x08048395 <main+9> : mov $0x0,%eax
0x0804839a <main+14>: sub %eax,%esp
------------------------------------------------------------------------------------
# char s333[] = "abc";
0x0804839c <main+16>: mov 0x80484df, %eax # 0x80484df处为"abc",位于.rodata
0x080483a1 <main+21>: mov %eax, 0xfffffff8(%ebp) # 0xfffffff8(%ebp) 为局部变量 char s333[]
------------------------------------------------------------------------------------
# char *p333 = "123456";
0x080483a4 <main+24>: movl $0x80484e3, 0xfffffff0(%ebp) # 0x80484e3处为"123456/0",位于.rodata; 0xfffffff0(%ebp) 为局部
变量 char *p333
------------------------------------------------------------------------------------
# p111 = (char *)malloc(10);
0x080483ab <main+31>: sub $0xc, %esp
0x080483ae <main+34>: push $0xa # 0xa=10 ; push $0xa后,此时堆栈esp值又减去4字节,相当于sub 、
$0x10,%esp
0x080483b0 <main+36>: call 0x804829c <malloc>
0x080483b5 <main+41>: add $0x10, %esp
0x080483b8 <main+44>: mov %eax, 0x8049500 # 0x8049500 为全局变量p111,位于.data
------------------------------------------------------------------------------------
# p222 = (char *)malloc(20);
0x080483bd <main+49>: sub $0xc, %esp
0x080483c0 <main+52>: push $0x14 # 0x14=20 ; push $0xa后,此时堆栈esp值又减去4字节,相当于sub
$0x10,%esp
0x080483c2 <main+54>: call 0x804829c <malloc>
0x080483c7 <main+59>: add $0x10, %esp
0x080483ca <main+62>: mov %eax, 0xfffffff4(%ebp) # 0xfffffff4(%ebp) 为局部变量p222
------------------------------------------------------------------------------------
# strcpy(p111, "123456");
0x080483cd <main+65>: sub $0x8, %esp
0x080483d0 <main+68>: push $0x80484e3 # 0x80484e3处内容为"123456/0",位于.rodata;
0x080483d5 <main+73>: pushl 0x8049500 # 0x8049500 为全局变量p111,位于.data
0x080483db <main+79>: call 0x80482bc <strcpy>
0x080483e0 <main+84>: add $0x10, %esp
------------------------------------------------------------------------------------
0x080483e3 <main+87>: leave
0x080483e4 <main+88>: ret
0x080483e5 <main+89>: nop
0x080483e6 <main+90>: nop
0x080483e7 <main+91>: nop
0x080483e8 <main+92>: nop
0x080483e9 <main+93>: nop
0x080483ea <main+94>: nop
0x080483eb <main+95>: nop
0x080483ec <main+96>: nop
0x080483ed <main+97>: nop
0x080483ee <main+98>: nop
0x080483ef <main+99>: nop
End of assembler dump.
(gdb) q
[test@redhat]#
---------------------------------------------------------------------------------------------------------------------------
[test@redhat]# readelf -a ./aaa
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x80482d0
Start of program headers: 52 (bytes into file)
Start of section headers: 2040 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 7
Size of section headers: 40 (bytes)
Number of section headers: 27
Section header string table index: 24
Section Headers: Addr是文件加载进内存时,每个section在内存中的虚拟地址
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
-------------------------------------------------------------------------------------------------------------------------
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 08048114 000114 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 08048128 000128 000020 00 A 0 0 4
[ 3] .hash HASH 08048148 000148 00002c 04 A 4 0 4
[ 4] .dynsym DYNSYM 08048174 000174 000060 10 A 5 1 4
[ 5] .dynstr STRTAB 080481d4 0001d4 000053 00 A 0 0 1
[ 6] .gnu.version VERSYM 08048228 000228 00000c 02 A 4 0 2
[ 7] .gnu.version_r VERNEED 08048234 000234 000020 00 A 5 1 4
[ 8] .rel.dyn REL 08048254 000254 000008 08 A 4 0 4
[ 9] .rel.plt REL 0804825c 00025c 000018 08 A 4 b 4
[10] .init PROGBITS 08048274 000274 000017 00 AX 0 0 4
[11] .plt PROGBITS 0804828c 00028c 000040 04 AX 0 0 4
[12] .text PROGBITS 080482d0 0002d0 0001e4 00 AX 0 0 16
[13] .fini PROGBITS 080484b4 0004b4 00001b 00 AX 0 0 4
[14] .rodata PROGBITS 080484d0 0004d0 00001a 00 A 0 0 4
[15] .eh_frame PROGBITS 080484ec 0004ec 000004 00 A 0 0 4
[16] .data PROGBITS 080494f0 0004f0 00001c 00 WA 0 0 4
[17] .dynamic DYNAMIC 0804950c 00050c 0000c8 08 WA 5 0 4
[18] .ctors PROGBITS 080495d4 0005d4 000008 00 WA 0 0 4
[19] .dtors PROGBITS 080495dc 0005dc 000008 00 WA 0 0 4
[20] .jcr PROGBITS 080495e4 0005e4 000004 00 WA 0 0 4
[21] .got PROGBITS 080495e8 0005e8 00001c 04 WA 0 0 4
[22] .bss NOBITS 08049604 000604 000008 00 WA 0 0 4
[23] .comment PROGBITS 00000000 000604 000126 00 0 0 1
[24] .shstrtab STRTAB 00000000 00072a 0000ce 00 0 0 1
[25] .symtab SYMTAB 00000000 000c30 0004c0 10 26 2f 4
[26] .strtab STRTAB 00000000 0010f0 000275 00 0 0 1
------------------------------------------------------------------------------------------------------------------------
Program Headers:
------------------------------------------------------------------------------------------------------------------------
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x08048034 0x08048034 0x000e0 0x000e0 R E 0x4
INTERP 0x000114 0x08048114 0x08048114 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.2]
LOAD 0x000000 0x08048000 0x08048000 0x004f0 0x004f0 R E 0x1000
LOAD 0x0004f0 0x080494f0 0x080494f0 0x00114 0x0011c RW 0x1000
DYNAMIC 0x00050c 0x0804950c 0x0804950c 0x000c8 0x000c8 RW 0x4
NOTE 0x000128 0x08048128 0x08048128 0x00020 0x00020 R 0x4
STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
------------------------------------------------------------------------------------------------------------------------
Symbol table '.symtab' contains 76 entries:
对于.exe文件,符号的Value 是将来加载进内存中的真实地址;对于.so文件Value需要重定位.
Num: Value Size Type Bind Vis Ndx Name
----------------------------------------------------------------------------------------------------------------
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 08048114 0 SECTION LOCAL DEFAULT 1
2: 08048128 0 SECTION LOCAL DEFAULT 2
3: 08048148 0 SECTION LOCAL DEFAULT 3
4: 08048174 0 SECTION LOCAL DEFAULT 4
5: 080481d4 0 SECTION LOCAL DEFAULT 5
6: 08048228 0 SECTION LOCAL DEFAULT 6
7: 08048234 0 SECTION LOCAL DEFAULT 7
8: 08048254 0 SECTION LOCAL DEFAULT 8
9: 0804825c 0 SECTION LOCAL DEFAULT 9
10: 08048274 0 SECTION LOCAL DEFAULT 10
11: 0804828c 0 SECTION LOCAL DEFAULT 11
12: 080482d0 0 SECTION LOCAL DEFAULT 12
13: 080484b4 0 SECTION LOCAL DEFAULT 13
14: 080484d0 0 SECTION LOCAL DEFAULT 14
15: 080484ec 0 SECTION LOCAL DEFAULT 15
16: 080494f0 0 SECTION LOCAL DEFAULT 16
17: 0804950c 0 SECTION LOCAL DEFAULT 17
18: 080495d4 0 SECTION LOCAL DEFAULT 18
19: 080495dc 0 SECTION LOCAL DEFAULT 19
20: 080495e4 0 SECTION LOCAL DEFAULT 20
21: 080495e8 0 SECTION LOCAL DEFAULT 21
22: 08049604 0 SECTION LOCAL DEFAULT 22
23: 00000000 0 SECTION LOCAL DEFAULT 23
24: 00000000 0 SECTION LOCAL DEFAULT 24
25: 00000000 0 SECTION LOCAL DEFAULT 25
26: 00000000 0 SECTION LOCAL DEFAULT 26
27: 080482f4 0 FUNC LOCAL DEFAULT 12 call_gmon_start
28: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
29: 080495d4 0 OBJECT LOCAL DEFAULT 18 __CTOR_LIST__
30: 080495dc 0 OBJECT LOCAL DEFAULT 19 __DTOR_LIST__
31: 080484ec 0 OBJECT LOCAL DEFAULT 15 __EH_FRAME_BEGIN__
32: 080495e4 0 OBJECT LOCAL DEFAULT 20 __JCR_LIST__
33: 080494f8 0 OBJECT LOCAL DEFAULT 16 p.0
34: 08049604 1 OBJECT LOCAL DEFAULT 22 completed.1
35: 08048320 0 FUNC LOCAL DEFAULT 12 __do_global_dtors_aux
36: 08048360 0 FUNC LOCAL DEFAULT 12 frame_dummy
37: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
38: 080495d8 0 OBJECT LOCAL DEFAULT 18 __CTOR_END__
39: 080495e0 0 OBJECT LOCAL DEFAULT 19 __DTOR_END__
40: 080484ec 0 OBJECT LOCAL DEFAULT 15 __FRAME_END__
41: 080495e4 0 OBJECT LOCAL DEFAULT 20 __JCR_END__
42: 08048490 0 FUNC LOCAL DEFAULT 12 __do_global_ctors_aux
43: 00000000 0 FILE LOCAL DEFAULT ABS aaa.c
44: 08049504 4 OBJECT LOCAL DEFAULT 16 c444 # static变量为LOCAL绑定属性(也即作用域) 已初始化静态变量存放
在.data
45: 08049508 4 OBJECT LOCAL DEFAULT 16 c444.0 # 已初始化静态变量存放在.data (多个源文件可以定义同名的静态
变量)
46: 08049608 4 OBJECT LOCAL DEFAULT 22 c555 # 未初始化静态变量存放在.bss
47: 080494fc 4 OBJECT GLOBAL DEFAULT 16 a111 # 全局变量为GLOBAL绑定属性 已初始全局变量存放在.data
48: 0804950c 0 OBJECT GLOBAL DEFAULT 17 _DYNAMIC
49: 080484d0 4 OBJECT GLOBAL DEFAULT 14 _fp_hw
50: 080494f0 0 NOTYPE GLOBAL HIDDEN ABS __fini_array_end
51: 080494f4 0 OBJECT GLOBAL HIDDEN 16 __dso_handle
52: 08048440 66 FUNC GLOBAL DEFAULT 12 __libc_csu_fini
53: 08048274 0 FUNC GLOBAL DEFAULT 10 _init
54: 0804829c 427 FUNC GLOBAL DEFAULT UND malloc@@GLIBC_2.0
55: 080482d0 0 FUNC GLOBAL DEFAULT 12 _start
56: 080494f0 0 NOTYPE GLOBAL HIDDEN ABS __fini_array_start
57: 080483f0 71 FUNC GLOBAL DEFAULT 12 __libc_csu_init
58: 08049500 4 OBJECT GLOBAL DEFAULT 16 p111
59: 08049604 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
60: 0804838c 89 FUNC GLOBAL DEFAULT 12 main
61: 080482ac 251 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC _
62: 080494f0 0 NOTYPE GLOBAL HIDDEN ABS __init_array_end
63: 080494f0 0 NOTYPE WEAK DEFAULT 16 data_start
64: 080484b4 0 FUNC GLOBAL DEFAULT 13 _fini
65: 080494f0 0 NOTYPE GLOBAL HIDDEN ABS __preinit_array_end
66: 08049604 0 NOTYPE GLOBAL DEFAULT ABS _edata
67: 080495e8 0 OBJECT GLOBAL DEFAULT 21 _GLOBAL_OFFSET_TABLE_
68: 0804960c 0 NOTYPE GLOBAL DEFAULT ABS _end
69: 080494f0 0 NOTYPE GLOBAL HIDDEN ABS __init_array_start
70: 080484d4 4 OBJECT GLOBAL DEFAULT 14 _IO_stdin_used
71: 080494f0 0 NOTYPE GLOBAL DEFAULT 16 __data_start
72: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
73: 080494f0 0 NOTYPE GLOBAL HIDDEN ABS __preinit_array_start
74: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
75: 080482bc 48 FUNC GLOBAL DEFAULT UND strcpy@@GLIBC_2.0
==================================Q & A===========================================
1. static全局变量与普通的全局变量有什么区别 ?
全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。
全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。
这两者的区别在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
static全局变量只初使化一次,防止在其他文件单元中被引用;
2. static局部变量和普通局部变量有什么区别 ?
把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。
static局部变量只被初始化一次,下一次依据上一次结果值;
3. static函数与普通函数有什么区别?
static函数与普通函数作用域不同,仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件.
static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝
程序的内存分配----变量在可执行文件中的内存区分配相关推荐
- C++中运行一个程序的内存分配情况及qt中的内存管理机制
一个由c/C++编译的程序占用的内存分为以下几个部分 1.栈区(stack)- 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等.其操作方式类似于数据结构中的栈. 2.堆区(heap) - 一 ...
- java 内存堆和栈_java中堆内存与栈内存的知识点总结
一.概述 在Java中,内存分为两种,一种是栈内存,另一种就是堆内存. 二.堆内存 1.什么是堆内存? 堆内存是Java内存中的一种,它的作用是用于存储Java中的对象和数组,当我们new一个对象或者 ...
- 从内存溢出看Java 环境中的内存结构
作为有个java程序员,我想大家对下面出现的这几个场景并不陌生,倍感亲切,深恶痛绝,抓心挠肝,一定会回过头来问为什么为什么为什么会这样,嘿嘿,让我们看一下我们日常在开发过程中接触内存溢出的异常: Ex ...
- python内存管理 变量无需事先声明_python 内存管理
内存管理 包括: 变量无须事先声明 变量无须指定类型 不用关心内存管理 变量名会被"回收" del 语句能够直接释放资源 变量定义 python中, 变量在第一次被赋值时自动声明, ...
- 查看linux电脑总内存,如何查看Linux系统中的内存使用情况的命令呢?
有些命令可用于检查Linux系统中的内存使用情况.这是一些更好的命令. 有很多工具可以查看Linux系统中的内存使用情况.一些命令被广泛使用,例如free和ps.其他命令允许以多种方式显示系统的性能统 ...
- new arraylist内存_如何避免内部类中的内存泄漏
我先假设读者已经熟悉在Java代码中使用嵌套类的基础知识.在本文里,我将展示嵌套类的陷阱,内部类在JVM中引起内存泄漏和内存不足错误的地方.之所以会发生这种类型的内存泄漏,是因为内部类必须始终能够访问 ...
- linux java 进程内存_linux – 在java进程中消耗内存的是什么?
我们正在尝试在中等负载下研究 java进程的内存使用情况. PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 12663 test 20 0 ...
- python内存管理 变量无需指定类型_Python内存管理
到现在为止,你已经看了不少Python代码的例子.我们本节的主题是变量和内存管理的细节,包括: 变量无需事先声明. 变量无需指定类型. 程序员不用关心内存管理. 变量名会被"回收" ...
- mips 内存 linux,MIPS 在linux中的内存映射
如图: 一些说明: 1.内核从什么地方开始运行:MIPS Linux内核的代码构建为在kseg0区运行:虚拟地址从0x80000000向上.这个范围的地址仅仅是一个到物理内存低512MB的窗口,无需T ...
最新文章
- android与单片机wifi通信原理图,用LT8920做2.4G无线通信原理图与51单片机程序
- MySQL语句相关经验总结
- 【DIY】热水器升级加装远程wifi控制功能,esp8266远程红外控制热水器启动,稳定连续运行4天了,功能展示终稿...
- 最小货架剩余寿命\总货架寿命\最大仓储时间
- 编程实现将一个N进制数转换成M进制数
- plc梯形图语言c1,plc梯形图编程语言是什么?
- 你需要administrators提供的权限才能删除_终于解决了:你需要来自XXX的权限才能对此文件进行更改
- 推荐第三方SQL查询工具
- PLC控制步进电机在机床自动线中的应用
- Atitit 编程语言编程方法的进化演进 sp COP ,AOP ,SOP
- 在网站中使用VideoJs视频播放器播放视频
- 合肥Android两天十面总结
- 电脑硬件知识入门之显卡篇
- 编码格式问题 错误:JSON parse error: Invalid UTF-8 middle byte 0x3f
- python中turtle隐藏画笔_python中Turtle的画笔命令有哪些?
- halcon_halcon图像处理基本运算
- 计算机超级工作站,【八核高性能计算超级计算机CAECADCAM有限元超级工作站】.docx...
- NASA授予下一代航天计算处理器合同,中国情况如何?
- 丰巢后撤,便宜了菜鸟驿站、京东快递柜?
- 一个网站SEO优化方案
热门文章
- pytorch---之指定GPU
- Git基础教程(四)
- java encode乱码_java 中文乱码问题的解决
- 考研c语言复试常问问题,2018考研复试常问的十个问题及回答指导
- python的django_真正搞明白Python中Django和Flask框架的区别
- file android closed,Reading a json file in Android [closed]
- 计算机主机制作过程,计算机主机箱的制作方法
- 样本修改 sample_如何在R中使用sample()获取样本?
- 如何检查Java中是否存在文件
- python参数检查类型_Python类型检查