C语言的内存管理
堆和栈的区别:
栈的特征
执行的速度相对较快;
空间较小;
生存期由系统决定;
作用域较小;
有名空间,可以通过变量名或者数据名访问;
堆的特征
执行的速度相对较慢;
空间较大;
生存期由“自己”决定,malloc申请,free释放;
作用域很大(整个程序都可以访问);
无名空间,只能通过指针使用;
C语言空间的申请
malloc
功能:
分配 size 字节的未初始化内存。若分配成功,则返回指向分配内存块最低位(首位)字节的,为任何拥有基础对齐的对象类型对齐的指针。
头文件:
#include<stdlib.h>
void* malloc( size_t size );
参数:
size - 要分配的字节数
返回值:
成功时:返回指向新分配内存的指针。为避免内存泄漏,必须用 free() 或 realloc() 解分配返回的指针。
失败时:返回空指针。
说明:
malloc申请的空间为连续空间;malloc申请的是没有初始化的空间;
返回值类型是void * 该类型表明malloc返回的地址空间中的数据类型是不确定,必须经过强制类型转换才可以使用。
realloc
功能:
先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域(注意:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。
头文件:
#include <stdlib.h>
extern void *realloc(void *mem_address, unsigned int newsize);
参数:
mem_address - 当前的指针
newsize - 重新分配空间的大小
返回值:
如果重新分配成功则返回指向被分配内存的指针,否则返回空指针NULL。
说明:
如果mem_address为NULL,则realloc()和malloc()类似。分配一个newsize的内存块,返回一个指向该内存块的指针。
如果newsize大小为0,那么释放mem_address指向的内存,并返回NULL。
如果没有足够可用的内存用来完成重新分配(扩大原来的内存块或者分配新的内存块),则返回NULL。而原来的内存块保持不变。
假如原来的内存后面还有足够多剩余内存的话,realloc的内存=原来的内存+剩余内存,realloc还是返回原来内存的地址; 假如原来的内存后面没有足够多剩余内存的话,realloc将申请新的内存,然后把原来的内存数据拷贝到新内存里,原来的内存将被free掉,realloc返回新内存的地址
如果size为0,效果等同于free()。这里需要注意的是只对指针本身进行释放,例如对二维指针**a,对a调用realloc时只会释放一维,使用时谨防内存泄露。
传递给realloc的指针必须是先前通过malloc(), calloc(), 或realloc()分配的
传递给realloc的指针可以为空,等同于malloc。
实例:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char *str; /* 最初的内存分配 */ str = (char *) malloc(15); strcpy(str, "runoob"); printf("String = %s, Address = %p\n", str, str); /* 重新分配内存 */ str = (char *) realloc(str, 25); strcat(str, ".com"); printf("String = %s, Address = %p\n", str, str); free(str); return(0); }
calloc
功能:
在内存的动态存储区中分配num个长度为size的连续空间;
头文件:
#include <stdlib.h>
void* calloc(unsigned int num,unsigned int size);
参数:
num - 对象个数
size - 对象占据的内存字节数,相较于malloc函数,calloc函数会自动将内存初始化为0
返回值:
成功时,返回指向新分配内存的指针。为避免内存泄漏,必须用 free() 或 realloc() 解分配返回的指针。
失败时,返回空指针。
说明:
因为对齐需求的缘故,分配的字节数不必等于 num*size 。
初始化所有位为零不保证浮点数或指针被各种初始化为 0.0 或空指针(尽管这在所有常见平台上为真)。
因为calloc()函数会清空分配的内存,而malloc()函数不会,所以可以调用以“1”作为第一个实参的calloc()函数,为任何类型的数据项分配空间。
实例:
#include<stdio.h> #include<stdlib.h> int main() { int i; int* pn = (int*)calloc(10, sizeof(int)); for(i = 0;i < 10;i++) printf("%d", pn[i]); printf("\n"); free(pn); return 0; }
alloca
功能:
alloc是在栈(stack)上申请空间,该变量离开其作用域之后被自动释放,无需手动调用释放函数。
头文件:
#include<stdlib.h>
原型:
void * __cdecl alloca(size_t);
参数:
size - 要分配的字节数
返回值:
函数返回一个指向申请到的空间的void型指针.
说明:
在某些系统中会宏定义成_alloca使用.
不提倡使用此函数:
在调用 alloca() 的函数返回的时候, 它分配的内存会自动释放。也就是说, 用 alloca 分配的内存在某种程度上局部于函数的“堆栈帧”或上下文中。
alloca() 不具可移植性, 而且在没有传统堆栈的机器上很难实现。 当它的返回值直接传入另一个函数时会带来问题, 如 fgets(alloca(100), 100, stdin)。
由于这些原因, alloca() 不合标准, 不宜使用在必须广泛移植的程序中, 不管它可能多么有用。 既然 C99 支持变长数组(VLA), 它可以用来更好的 完成 alloca() 以前的任务。
C语言空间的释放
free
原型:
void free(void *ptr);
功能:
释放之前调用 calloc、malloc 或 realloc 所分配的内存空间。
头文件:
#include <stdlib.h>
参数:
ptr - 要释放的空间的指针
返回值:
该函数不返回任何值。
说明:
不能传NULL;
不能释放已经被释放的空间;
不能使用已经被释放的空间;
当程序运行过程中申请了空间,但是没有free的话,会造成内存泄漏.一部分的内存没有被使用,但是由于没有free,因此系统认为这部分内存还在使用,造成不断的向系统申请内存,使得系统可用内存不断减少.但是内存泄漏仅仅指程序在运行时,程序退出时,OS将回收所有的资源.因此,适当的重起一下程序,有时候还是有点作用.
malloc函数详细说明
在C语言中只能通过malloc()和其派生的函数进行动态的申请内存,而实现的根本是通过系统调用实现的(在linux下是通过sbrk()系统调用实现)。
malloc()到底从哪里得到了内存空间?答案是从堆里面获得空间。也就是说函数返回的指针是指向堆里面的一块内存。操作系统中有一个记录空闲内存地址的链表。当操作系统收到程序的申请时,就会遍历该链表,然后就寻找第一个空间大于所申请空间的堆结点,然后就将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。
malloc()在运行期动态分配分配内存,free()释放由其分配的内存。malloc()在分配用户传入的大小的时候,还分配的一个相关的用于管理的额外内存,不过,用户是看不到的。所以,实际的大小 = 管理空间 + 用户空间,malloc(0)的有效内存大小为24,32位中为12,准确的说是至少是这么多,并且这些内存是可以用的。
#include<stdio.h> #include<stdlib.h> int main() { char *p=(char *)malloc(0); char *p1=(char *)malloc(5); char *p2=(char *)malloc(25); char *p3 = (char *)malloc(39); char *p4 = (char *)malloc(41); printf("p size:%d\n",malloc_usable_size(p)); printf("p1 size:%d\n", malloc_usable_size(p1)); printf("p2 size:%d\n", malloc_usable_size(p2)); printf("p3 size:%d\n", malloc_usable_size(p3)); printf("p4 size:%d\n", malloc_usable_size(p4)); free(p); free(p1); free(p2); free(p3); free(p4); return 0; }
此外,堆中的内存块总是成块分配的,并不是申请多少字节,就拿出多少个字节的内存来提供使用。堆中内存块的大小通常与内存对齐有关(8Byte(for 32bit system)或16Byte(for 64bit system)。
因此,在64位系统下,当(申请内存大小+sizeof(struct mem_control_block) )% 16 == 0的时候,刚好可以完成一次满额的分配,但是当其!=0的时候,就会多分配内存块。