程序在运行的时候,其内存的来源主要通过三种方法: 栈 堆 数据段,总体上来讲栈是一般用来存放小内存的局部变量,堆内存和数据段的属性很像,在使用的的时候,如果这个变量是伴随程序一直存在则使用全局变量,也就是放在数据段,如果一个变量使用完了就没用了,那么就适合用堆内存(先申请,然后释放即可),
一:栈(stack):
1:栈在使用的时候是编译器自动分配内存空间的,不需要程序员的干涉,其次栈的大小是有限的,所以当我们定义的变量需要大片的内存的时候就不适合使用栈,
2:栈存放的是普通变量,栈的在使用的时候是反复使用的,所以栈内存是脏的,在定义普通变量的时候,如果不对变量进行初始化,那么变量的值就是随机的。
3:栈是先进后出的,其内存空间是向下增长的。
二:堆(heap):
1:堆得使用时是由程序员来操作的,程序员通过malloc来向内存申请堆内存,使用完以后通过free来释放这部分内存,如果这部分内存在使用完以后没有进行内存的释放,那么这部分内存管理器就会认为这部分内存一直被占用的,体现出来的就是程序吃内存,也就是所谓的内存泄漏,如果没有释放内存,则被占用的内存只有当程序结束以后才会被释放。一般需要大片的内存时才使用堆,堆是先进先出的,其内存空间是向上增长的。
代码示例:
1
|
<span style=
"font-family:'宋体', SimSun;font-size:20px;"
>#include <stdio.h><br data-filtered=
"filtered"
>#include <stdlib.h><br data-filtered=
"filtered"
>
int
main(
void
)<br data-filtered=
"filtered"
>{<br data-filtered=
"filtered"
>
//申请内存<br data-filtered="filtered">int *p = (int *)malloc(100);<br data-filtered="filtered">if(p == NULL) //错误检查<br data-filtered="filtered">{<br data-filtered="filtered">printf("error \n");<br data-filtered="filtered">return -1;<br data-filtered="filtered">}<br data-filtered="filtered">//内存使用S<br data-filtered="filtered">*(p+4) = 1024;<br data-filtered="filtered">*(p+3000) = 111;<br data-filtered="filtered">printf("*(p+3) = %d\n", *(p+4));<br data-filtered="filtered">printf("*(p+30000)) = %d\n", *(p+3000));<br data-filtered="filtered">printf("p = %p \n",p);<br data-filtered="filtered">printf("(p +4) = %p \n",(p +4));<br data-filtered="filtered"><br data-filtered="filtered">free(p); //释放内存<br data-filtered="filtered"><br data-filtered="filtered">printf("*(p+3) = %d\n", *(p+4));<br data-filtered="filtered">printf("*(p+30000)) = %d\n", *(p+3000));<br data-filtered="filtered">printf("p = %p \n",p);<br data-filtered="filtered">printf("(p +4) = %p \n",(p +4));<br data-filtered="filtered"><br data-filtered="filtered">p = NULL; //避免野指针<br data-filtered="filtered"><br data-filtered="filtered">return 0;<br data-filtered="filtered">}</span>
|
运行结果:
*(p+3) = 1024
*(p+30000)) = 111
p = 0x8efb008
(p +4) = 0x8efb018
*(p+3) = 1024
*(p+30000)) = 111
p = 0x8efb008
(p +4) = 0x8efb018
分析:
1:堆内存可以越界访问,但是实际中最好还是不要,因为你在使用的时候越界访问就意味着踩到别人了
2:堆内存在释放之后还可以访问,并且访问的值还是不变的,说明堆内存也是脏的,堆内存释放的时候并没有对使用过的没存进行清理。
三:数据段(.data )
1:一个程序主要有数据段(.data) 代码段 和bss段,不同的段具有不同的段属性
数据段:(又叫数据区、静态数据区、静态区)数据段存放的是程序的中显示初始化的全局变量(不包括初始化为0的全局变量),同时需要注意的是全局变量才算是程序的数据,局部变量不是程序变量,只是函数数据
代码段:代码段就是程序中的可执行部分,直观理解代码段就是函数堆叠组成的。代码段是只读的。
bss段:(又叫zero initial 段)bss段存放的是为显示初始化的全局变量已经初始化为0的全局变量(C语言中默认全局变量的初始化就是为0),所以可以理解为bss段就是初始化为0的数据段。
2:放在.data段的变量有两种:第一种是显式初始化为非0的全局变量,另一种是静态局部变量,也就是static修饰的局部变量(普通变量是分配在栈上面,静态局部变量是分布在.data段)
四:代码段中的特殊数据
1:C语言使用char *p = "linux";定义字符串的时候,字符串"linux"实际上是被分配到了代码段上面,换句话说这里的字符串"linux"实际上是一个常量字符串而不是变量字符串。
2:const修饰的类型常量,const的实现方法主要有两种,一种是编译的时候将const修饰的变量放在代码段去实现不可修改(因为代码段是只读的),另一种是由编译器来检查来确保const修饰的常量,但是这种实际上和普通常量样,也是被放在了数据段,所以如果绕过编译器就可以实现修改,具体实现方法是用指针去指向这个常量的内存空间,然后解引用去修改这个常量,gcc中就是这样实现的。
本文转自 菜鸟养成记 51CTO博客,原文链接:http://blog.51cto.com/11674570/1861671