一,三种内存来源(程序可以操控的内存空间)
1,在一个C程序中,能够获取的内存就是三种情况:栈(stack)、堆(heap)、数据区(.data)。
二,栈的详解
1,栈的内存空间,程序运行时自动分配&自动回收,栈是自动管理的,程序员不需要手工干预。
2,栈内存在程序中就是那一块大小有限的内存空间,程序反复使用这一块空间。不断的在这一块空间中自动分配&“自动回收”。
3,由于栈内存反复使用,并且每次使用后程序不会去清理之间使用的空间,因此分配到时保留原来的值,是一块脏内存。所以在定义一个分布在栈上的局部变量时,一定要先初始化,不然其值就是随机的。
4,函数不能返回一个栈变量的指针,因为这个空间时临时的。
5,栈会溢出,因为操作系统事先给定了栈的大小,如果在函数中无穷尽的分配栈内存总能用完。
三,堆内存的详解
1,操作系统堆管理器管理堆内存,堆管理器是操作系统的一个模块,堆管理内存分配灵活,按需分配。
2,程序手动申请和释放,需要程序员用malloc申请,free释放。
3,堆内存也是反复使用的,而且使用者用完释放前不会清除,因此也是脏的。
4,堆内存只在malloc和free之间属于一个进程,可以访问,在malloc之前和free之后都不能访问,否则会出现不可与预料的错误。
四,堆内存使用 void *malloc(size_t size); void free(void *ptr);
1,malloc返回的是一个void * 类型的指针,实质上malloc返回的时堆管理器分配的本次申请的那段内存空间的首地址(返回的值其实是一个数字,这个数字表示一个内存地址)。
2,为什么要使用void *类型呢?
主要原因是:malloc帮我们分配内存时只是分配了内存空间,至于这段空间被用来存储什么类型的元素malloc是不关心的,由程序自己决定。
3,什么是void *类型?
void 类型不表示没有类型,而表示万能类型。void的意思是这个数据的类型当前是不确定的,在需要的时候可以再去指定它的具体类型。void *类型是一个指针类型,这个指针本身占四个字节,但是指针指向的类型是不确定的,换句话说,这个指针在需要的时候可以被强制转化成任何一种确定类型的指针,也就是说这个指针可以指向任何类型的变量。
4,malloc的返回值:成功申请空间后返回这个内存空间的指针,申请失败时返回NULL。所以malloc获取的内存使用前一定要先检验是否为NULL。
5,malloc申请的内存用完后要free释放。free会告诉堆管理器这段内存我用完了你可以回收了,堆管理器回收了这段内存后这段内存当前进程就不应该再使用了。因为释放后堆管理器就可能把这段内存分配给其他进程。
6,在调用free释放这段内存前,指向这段内存的指针一定不能丢(也就是不能给p赋其他值)。因为一旦p丢失,这段malloc来的内存就永远的丢失了,直到当前程序结束时,操作系统才能回收这段内存。
五,malloc的一些细节
1,malloc(0);返回的时NULL还是一个有效的指针?
实际分配了32字节的一段内存,并且返回了这段内存的地址。这个答案是不确定的,因为C语言并没有明确规定malloc(0)的具体表现,由各个平台malloc函数库的实现者来定义。
2,malloc(4);gcc中malloc默认最小是以32字节为单位分配的。如果malloc小于32B大小时,会返回一个32字节大小的内存。malloc实现时没有实现任意自己的分配,而是允许一些大小的块内存的分配。也就是以某个字节的块内存为单位分配。
64位平台
#include<stdio.h> #include<stdlib.h> #pragma pack(4) int main(void) { #if 0 char *p = malloc(0); char *a = malloc(1); if(NULL == p) { printf(" malloc error "); } printf(" p = %p ",p); //p = 0xc73010 printf(" a = %p ",a); //a = 0xc73030 printf(" p size = %d ",a-p); //32 #else char *p = malloc(4); char *a = malloc(1); if(NULL == p) { printf(" malloc error "); } printf(" p = %p ",p); //p = 0x2497010 printf(" a = %p ",a); //a = 0x2497030 printf(" p size = %d ",a-p); //32 #endif } #pragma pack()
六,代码段、数据段、bss段
1,编译器在编译程序的时候,将程序中所有的元素分成了一些组成部分,各部分构成一个段,所以说段是可执行程序的组成部分。
2,代码段:代码段就是程序中的可执行部分,就是函数堆叠组成的,里面是一个个的函数定义。char *p = "linux;定义字符串时,字符串”linux“实际上被分配在代码段。const型常量,有时也是被放在代码段。
3,数据段(也被称为数据区、静态数据区、静态区):数据段就是程序中的数据。显示初始化为非零的 全局变量和static修饰的静态局部变量放在数据段。
4,.bss段:未初始化或显示初始化为零的 全局变量和静态局部变量放在.bss段。
七,const常量
1,C语言种使用const关键字来定义常量,常量就是不能被改变的量。
2,const的实方法至少有两种:
第一种,就是编译时将const修饰的变量放在代码段,以实现不能修改(常见于各种单片机的编译器)。
第二种,就是由编译器来检查以确保const型的常量不能被修改,实际上const型常量还是和普通变量一样放在数据段中(gcc中就是这样实现的),这个时候const 常量实际上是可以被修改掉的,只要骗过编译器的类型检查。
八,总结
1,相同点:三种获取内存的方法,都可以给程序提供内存,都可以用来定义变量给程序用。
2,不同点:栈内存对应的是C种普通的局部变量(别的变量还用不了栈,而且栈是自动的,由编译器和运行时的环境共同来提供服务,程序员无法手工控制),堆内存完全是独立于程序存在和管理的(通过API由堆管理器管理),程序需要内存时可以手工申请malloc,使用完成后要free;数据段对于程序来说对应程序中的全局变量和静态局部变量。