一、
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 bss
24 .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 \\.data
23 .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"); //出错
}