内存管理——数据存放位置和野指针

来源:互联网 发布:旱雪 知乎 编辑:程序博客网 时间:2024/06/05 23:46

一、

1、未初始化的全局变量(.bss段)

bss段用来存放那些没有初始化和初始化为0的全局变量

int bass_array[1024 * 1024];int main(int argc, char* argv[]){    return 0;}#gcc -g  bss.c -o bss.exe#ls -l bss.exe-rwxrwxr-x 1 root root 5975 Nov 16 09:32 bss.exe#objdump -h bss.exe | grep bss24 .bss 00400020 080495e0 080495e0 000005e0 2**5
变量bss_array的大小为4MB,而可执行文件大小只有5KB。由此可见,bss类型的全局变量只占运行时的内存空间,而不占用文件空间。

现代大多数操作系统,在加载程序时,会把所有的bss全局变量清0。作为全局变量,在整个程序运行周期内,bss数据段一直存在。


2、初始化过的全局变量(.data段)

data段用来存放那些初始化为非零的全局变量。

int data_array[1024 * 1024] = {1};int main (int argc, char* argv[]){   return 0;}#ls -l data.exe-rwxrwxr-x 1 root root 4200313 Nov 16 09:34 data.exe#objdump -h data.exe |grep \\.data23 .data 00400020 080495e0 080495e0 000005e0 2**5
仅仅是把初始化的值改为非0了,文件变为4MB多,由此可见,data类型的全局变量既占文件空间,又占用运行时的内存空间。同样作为全局变量,在整个程序的运行周期内,data数据是一直存在的。


3、常量数据(.rodata段) 

rodata用来存放常量数据的


4、代码(.text段)

text段存放代码(如函数)和部分整数常量,它与rodata段很相似,不过这个段是可执行的。


5、栈(stack)

栈用于存放临时变量和函数参数。

实现递归操作,用栈是最优雅的方式。

尽管大多数编译器在优化时,会把常用的参数或者局部变量放入寄存器中。但用栈来管理函数调用时的临时变量(局部变量和参数)是通用的做法,前者只是辅助手段,且只在当前函数中使用,一旦调用下一层函数,这些值仍然要存入栈中才行


6、堆(heap)

堆是最灵活的一种内存,它的生命周期完全由使用者控制。

标准C语言提供以下几个函数:

malloc:用来分配一块指定大小的内存

realloc:用来调整/重分配一块存在的内存

free:用来释放不再使用的内存。

1)malloc/free 要配对使用。

内存分配了不释放,称为内存泄漏,内存泄漏了迟早会出现Out of memory的错误,在分配内存就会失败。释放时也只能释放分配出来的内存,释放无效内存或者重复free都是不行的,会造成程序crash。

分配多少内存用多少,分配了100MB就只能用100B,不管是读是写,都只能在这范围中,读多了会读到随机数据,写多了会造成随机的破坏,这种情况称为缓冲溢出。

手工检查有没有内存泄漏或者缓冲溢出时很困难的,幸好有些工具可以使用,如Linux下有valgrind,每次写完程序都应该用valgrind跑一遍

2)每个区间都有四个属性

r表示可读取

w表示可修改

x表示可以执行

p/s表示是否为共享内存

有文件的内存空间,属性为r-p表示存放的是rodata,属性rw-p表示存放的是bss和data,属性为r-xp表示存放的是text数据;没有文件名的内存区间,表示用mmap映射的匿名空间;文件名为【stack】内存区间表示栈;文件名为【heap】的内存区间表示堆


二、内存分配方式(三种)

1)从静态存储区域分配。内存在程序编译时就已经分配好,这块内存在程序的整个运行期间都存在,如全局变量、static变量等。

2)在栈上创建。在执行函数时,函数内部局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算使用内置于处理器的指令集,效率很高,但分配内存容量有限。

3)从堆上分配,亦称动态内存分配。程序在运行时用malloc或new申请所需要的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由程序员决定,使用非常灵活,问题也多。

全局变量和static变量是整个程序需要用到的,单独分出一块存储区保存,在程序的整个运行期间该存储区存储数据不清空;局部变量在函数退出时自动清空,放在栈(stack)里进行临时存储。用指令new和malloc分配内存需要自己在堆中手动申请并用free和delete指令手动释放。



三、野指针:不是NULL指针,是指向“垃圾”内存的指针。一般不会错用NULL指针,因为用if语句很容易判断。但是野指针是很危险的,if语句对它不起作用。

野指针的成因主要有两种。

1)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的默认值是随机的,它会乱指一气。所以,指针变量在创建时同时应当被初始化,要么将指针设置为NULL,要么让它指向合法内存。

2)指针p被free或者delete之后,没有设置NULL,让人误以为p是个合法的指针。别看free和delete的名字“恶狠狠的”(尤其是delete),它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。用调试跟踪,发现指针p被free以后其地址仍然不变(非NULL),只是该地址对应的内存是垃圾,p成了“野指针”。如果此时不把p设置为NULL,会让人误以为p是个合法的指针。

char *p = (char *) malloc (100);strcpy(p,"hello");free(p);                            //p所指的内存被释放,但是p所指的地址仍然不变。。。if (p != NULL)                      //没有起到防错作用{    strcpy(p,"world");              //出错}


1 0
原创粉丝点击