EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
本帖最后由 行者~ABC 于 2020-4-28 09:52 编辑 3 T: |' V5 \2 e- Z {8 t3 Z/ ^
& p* | A: C( {一、五大内存分区
[7 Q; g( l1 { O
5 } H- {6 s+ h; f3 Y1 E内存分成5个区,它们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。2 ?- A# |* {) r- D V
1、栈区(stack):FIFO就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。里面的变量通常是局部变量、函数参数等。
0 f. F: r4 x J, v& Q7 ?- n) m& g2、堆区(heap):就是那些由new分配的内存块,它们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。; t" x# j8 W! ~4 K7 `
3、自由存储区:就是那些由malloc等分配的内存块,它和堆是十分相似的,不过它是用free来结束自己的生命。
, p+ S5 Y# k/ F4 o1 t4、全局/静态存储区:全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。+ X9 ?! h/ K$ d) {) {& n
5、常量存储区:这是一块比较特殊的存储区,它们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改,而且方法很多)
" X0 |, L7 z3 O0 a# J9 h) Jcode/data/stack8 J0 g$ o+ w0 ~! Q g* b) c
内存主要分为代码段,数据段和堆栈。代码段放程序代码,属于只读内存。数据段存放全局变量,静态变量,常量等,堆里存放自己malloc或new出来的变量,其他变量就存放在栈里,堆栈之间空间是有浮动的。数据段的内存会到程序执行完才释放。调用函数先找到函数的入口地址,然后计算给函数的形参和临时变量在栈里分配空间,拷贝实参的副本传给形参,然后进行压栈操作,函数执行完再进行弹栈操作。字符常量一般放在数据段,而且相同的字符常量只会存一份。0 ^ x S6 v- } E1 n) b! Q
二、C语言程序的存储区域% {% m1 P- n5 s; ^4 V. q
1、由C语言代码(文本文件)形成可执行程序(二进制文件),需要经过编译-汇编-连接三个阶段。编译过程把C语言文本文件生成汇编程序,汇编过程把汇编程序形成二进制机器代码,连接过程则将各个源文件生成的二进制机器代码文件组合成一个文件。
% \( ]. l4 ]( X5 T2、C语言编写的程序经过编译-连接后,将形成一个统一文件,它由几个部分组成。在程序运行时又会产生其他几个部分,各个部分代表了不同的存储区域:5 z7 ~& U! q/ q% j: S9 Z
1)代码段(Code或Text)
* [+ U m# Q t. k; a: d代码段由程序中执行的机器代码组成。在C语言中,程序语句执行编译后,形成机器代码。在执行程序的过程中,CPU的程序计数器指向代码段的每一条机器代码,并由处理器依次运行。
7 v, X" B3 t( K& Z6 `! t2)只读数据段(RO data)
5 T) i2 W0 C$ x* x* D只读数据段是程序使用的一些不会被更改的数据,使用这些数据的方式类似查表式的操作,由于这些变量不需要更改,因此只需要放置在只读存储器中即可。8 Q) U: w7 @% U z2 \
3)已初始化读写数据段(RW data)
, \* U9 d P: v! U- \* t! M已初始化数据是在程序中声明,并且具有初值的变量,这些变量需要占用存储器的空间,在程序执行时它们需要位于可读写的内存区域内,并且有初值,以供程序运行时读写。 } X8 ?) \4 D' G, L. k& f
4)未初始化数据段(BBS)0 w5 y# B i1 _
未初始化数据是在程序中声明,但是没有初始化的变量,这些变量在程序运行之前不需要占用存储器的空间。
8 v# Y' G% b0 o5)堆(heap): P d( D# [3 p% d$ y! u9 \
堆内存只在程序运行时出现,一般由程序员分配和释放。在具有操作系统的情况下,如果程序没有释放,操作系统可能在程序(例如一个进程)结束后会后内存。; z. Y& v- T7 i, t
6)栈(statck)
0 p1 X! ~2 ]: P7 D: T% @2 B堆内存只在程序运行时出现,在函数内部使用的变量,函数的参数以及返回值将使用栈空间,栈空间由编译器自动分配和释放。
; O& I& u4 G* ~2 {- G+ C7 E+ z
0 J1 k& q% \6 A( w& ^/ G/ v
3、代码段、只读数据段、读写数据段、未初始化数据段属于静态区域,而堆和栈属于动区域。代码段、只读数据段和读写数据段将在连接之后产生,未初始化数据段将在程序初始化的时候开辟,而对堆和栈将在程序饿运行中分配和释放。
. C' Z) \) i+ L5 B3 U# d' q
M7 f% V- c2 `5 h; C4、C语言程序分为映像和运行时两种状态。在编译-连接后形成的映像中,将只包含代码段(Text)、只读数据段(R0 Data)和读写数据段(RW Data)。在程序运行之前,将动态生成未初始化数据段(BSS),在程序的运行时还将动态生成堆(Heap)区域和栈(Stack)区域。( ^" I4 {1 J0 L3 O8 v
注:
# E) e7 L Y9 }; ~1、一般来说,在静态的映像文件中,各个部分称之为节(Section),而在运行时的各个部分称之为段(Segment)。如果不详细区分,统称为段。
' ^% q9 U% o& R2、C语言在编译连接后,将生成代码段(TEXT),只读数据段(RO Data)和读写数据段(RW Data)。在运行时,除了上述三个区域外,还包括未初始化数据段(BBS)区域和堆(heap)区域和栈(Stack)区域。
/ ?, p# w& `$ I4 L三、C语言程序的段
) O+ x4 V% O5 Y) e; M. {! c1、段的分类
% x, b+ Y$ ]0 |3 ]每一个源程序生成的目标代码将包含源程序所需要表达的所有信息和功能。目标代码中各段生成情况如下:8 e5 H8 b8 {/ M. G
1)代码段(Code)
. ^8 g7 e9 G" N$ O代码段由程序中的各个函数产生,函数的每一个语句将最终经过编译和汇编生成二进制机器代码6 T, `' w. F- ^2 {# B% k) z
2)只读数据段(RO Data)
9 x3 u, G% j% T* [2 R" e8 i$ o只读数据段由程序中所使用的数据产生,该部分数据的特点在运行中不需要改变,因此编译器会将数据放入只读的部分中。C语言的一些语法将生成只读数据数据段。
0 {/ b# g: ~, d, S, L7 a# S2 Z2、只读数据段(RO Data)
0 J6 {; N) t& u7 V# z) [, Y; n只读数据段(RO Data)由程序中所使用的数据产生,该部分数据的特点是在运行中不需要改变,因此编译器会将数据放入只读的部分中。以下情况将生成只读数据段。/ m% @* y3 L/ i2 e& C
1)只读全局变量
# u9 A P, I5 d" c定义全局变量const char a[100]=”abcdefg”将生成大小为100个字节的只读数据区,并使用字符串“abcdefg”初始化。如果定义为const char a[]=”abcdefg”,没有指定大小,将根据“abcdefgh”字串的长度,生成8个字节的只读数据段。
% w$ r4 ]8 I# a/ L1 M2)只读局部变量/ {: [9 k; ?8 X# g% A3 Q
例如:在函数内部定义的变量const char b[100]=”9876543210”;其初始化的过程和全局变量。
; G5 @& m3 O0 d9 N1 P; v+ @3)程序中使用的常量% d- J" T4 d6 C. R
例如:在程序中使用printf("information\n”),其中包含了字串常量,编译器会自动把常量“information \n”放入只读数据区。 y8 \, B" }' c; Y! u, }: M) W
注:在const char a[100]={“ABCDEFG”}中,定义了100个字节的数据区,但是只初始化了前面的8个字节(7个字符和表示结束符的‘\0’)。在这种用法中,实际后面的字节米有初始化,但是在程序中也不能写,实际上没有任何用处。因此,在只读数据段中,一般都需要做完全的的初始化。) T1 L% X) g, Z/ f8 H0 l
3、读写数据段(RW Data)
7 ]8 f6 d# T5 A P4 I读写数据段表示了在目标文件中一部分可以读也可以写的数据区,在某些场合它们又被称为已初始化数据段。这部分数据段和代码,与只读数据段一样都属于程序中的静态区域,但是具有科协的特点。 C' Q" i$ V$ _: Y/ q6 k- d' q
1)已初始化全局变量' ^0 J, B) O; t9 E
例如:在函数外部,定义全局的变量char a[100]=”abcdefg”1 S& O% _9 w1 l6 `2 f R0 V
2)已初始化局部静态变量
- i, i% G/ b; A( W, n例如:在函数中定义static char b[100]=”9876543210”。函数中由static定义并且已经初始化的数据和数组将被编译为读写数据段。) Y# |, ?* c& d- S& H" C6 S0 }3 c
说明:, t1 o8 F; {; W2 q
读写数据区的特点是必须在程序中经过初始化,如果只有定义,没有初始值,则不会生成读写数据区,而会定义为未初始化数据区(BSS)。如果全局变量(函数外部定义的变量)加入static修饰符,写成static char a[100]的形式,这表示只能在文件内部使用,而不能被其他文件使用。' Y, s' ^, D0 V7 {1 ~
4、未初始化数据段(BSS)/ w) \6 [! A/ J' g
未初始化数据段常被称之为BSS(英文名为Block start by symbol的缩写)。与读写数据段类似,它也属于静态数据区。但是该段中数据没有经过初始化。因此它只会在目标文件中被标识,而不会真正称为目标文件中的一个段,该段将会在运行时产生。未初始化数据段只有在运行的初始化阶段才会产生,因此它的大小不会影响目标文件的大小。2 y& E% G8 }6 s2 F. o
四、在C语言的程序中,对变量的使用需要注意的问题" \) h6 d7 C7 j+ @2 p2 N& r! u" t
1、在函数体中定义的变量通常是在栈上,不需要在程序中进行管理,由编译器处理。' a7 k' l: i7 V f
2、用malloc,calloc,realoc等分配分配内存的函数所分配的内存空间在堆上,程序必须保证在使用后使用后freee释放,否则会发生内存泄漏。0 m2 i6 l2 i" w. N) }. S: k
3、所有函数体外定义的是全局变量,加了static修饰符后的变量不管在函数内部或者外部存放在全局区(静态区)。
+ l- ?% z0 ^5 h& M! y* c4、使用const定义的变量将放于程序的只读数据区。
; k) j9 @; f- w- n8 ^. d. y说明:
# C" w8 q( w8 a! {3 M在C语言中,可以定义static变量:在函数体内定义的static变量只能在该函数体内有效;在所有函数体外定义的static变量,也只能在该文件中有效,不能在其他源文件中使用;对于没有使用 static修饰的全局变量,可以在其他的源文件中使用。这些区别是编译的概念,即如果不按要求使用变量,编译器会报错。使用static 和没使用static修饰的全局变量最终都将放置在程序的全局去(静态去)。
% t0 K; x* d# A& D- s2 q( E- Q五、程序中段的使用
2 I0 v' k$ s; i) x: E% J; IC语言中的全局区(静态区),实际上对应着下述几个段:
% v3 ]' Y1 V) l2 F7 @. I只读数据段:RO Data读写数据段:RW Data未初始化数据段:BSS Data
6 p9 z4 G; K; p$ K, t一般来说,直接定义的全局变量在未初始化数据区,如果该变量有初始化则是在已初始化数据区(RW Data),加上const修饰符将放置在只读区域(RO Data).
, y- T; w: L) Z" c3 _% ]例如:9 R3 H) X- [. x5 J* O9 b+ B
const char ro[ ]=”this is a readonlydata”; //只读数据段,不能改变ro数组中的内容,ro存放在只读数据段。1 G0 m2 W+ ~7 N; ~
char rw1[ ]=”this is global readwrite data”; //已初始化读写数据段,可以改变数组rw1中的内容。应为数值/是赋值不是把”this is global readwrite data” 地址给了rw1,不能改变char rw1[ ]=”this is global readwrite data”; //已初始化读写数据段,可以改变数组rw1中的内容。应为数值/是赋值不是把”this is global readwrite data” 地址给了rw1,不能改变”this is global readwrite data”的数值。因为起是文字常量放在只读数据段中char bss_1[100];//未初始化数据段 const char *ptrconst = “constant data”; //”constant data”放在只读数据段,不能改变ptrconst中的值,因为其是地址赋值。ptrconst指向存放“constant data”的地址,其为只读数据段。但可以改变ptrconst地址的数值,因其存放在读写数据段中。
! Y* T- U" F) w7 `实例讲解:int main( ) { short b;//b放置在栈上,占用2个字节 char a[100];//需要在栈上开辟100个字节,a的值是其首地址 char s[]=”abcde”;//s在栈上,占用4个字节,“abcde”本身放置在只读数据存储区,占6字节。s是一个地址 //常量,不能改变其地址数值,即s++是错误的。 char *p1;//p1在栈上,占用4个字节 char *p2 ="123456";//"123456"放置在只读数据存储区,占7个字节。p2在栈上,p2指向的内容不能更 //改,但是p2的地址值可以改变,即p2++是对的。 static char bss_2[100]; //局部未初始化数据段 static int c=0 ; //局部(静态)初始化区 p1 = (char *)malloc(10*sizeof(char)); //分配的内存区域在堆区 strcpy(p1,”xxx”); //”xxx”放置在只读数据存储区,占5个字节 free(p1); //使用free释放p1所指向的内存 return 0; }
: W- n/ R/ G T' \) t/ A
B( C0 `+ g+ B- s* |说明:
9 S, z: u4 {% Q; }6 |. N t1、只读数据段需要包括程序中定义的const型的数据(如:const char ro[]),还包括程序中需要使用的数据如“123456”。对于const char ro[]和const char * ptrconst的定义,它们指向的内存都位于只读数据据区,其指向的内容都不允许修改。区别在于前者不允许在程序中修改ro的值,后者允许在程序中修改ptrconst本身的值。对于后者,改写成以下的形式,将不允许在程序中修改ptrconst本身的值:const char * const ptrconst = “const data”; : Q9 b9 M4 O, \4 v% O
|