上次笔记C语言的时候笔记到关于C语言的不同数据变量类型的存储方式。包括变量的存储位置,什么时候为其分配内存,未被初始化时的初始值,作用域,生存期等话题。并留了一个关于“堆”的动态内存分配问题。趁课余时间聊聊动态内存分配及使用动态内存分配时需要注意的问题。
为什么要动态内存分配
void* realloc(void* ptr, unsigned newsize);
void* malloc(unsigned size);v
oid* calloc(size_t nelem, size_t elsize);
三个函数都在stdlib.h函数库
malloc与calloc的区别为1块与n块的区别:malloc调用形式为(类型*)malloc(size):在内存的动态存储区中分配一块长度为"size"字节的连续区域,返回该区域的首地址。calloc调用形式为(类型*)calloc(n,size):在内存的动态存储区中分配n块长度为"size"字节的连续区域,返回首地址。realloc 不能保证重新分配后的内存空间和原来的内存空间指在同一内存地址, 它返回的指针很可能指向一个新的地址。
用malloc举一简单例子来说明其基本用法:
例malloc_1.c
#include
#include
#include
#define N 5
int main(void)
{
int i;
char *p;
p = (char*)malloc(N * sizeof(char));
//Note: check return value of malloc funciton
if(p == NULL){
printf("RAM allocate fialed\n");
return 0;
}
//memset(p, '1', N * sizeof(char));
//Look p[] values before initilized
//and see address of everyone
for(i = 0; i < N; i++){
printf("p[%d] no_value:%c\taddress is %p\n", i, p[i], &p[i]);
}
//Release
free(p);
return 0;
}
编译及运行结果:
gcc -Wall malloc_1.c -o malloc_1 && ./malloc_1
p[0] no_value: address is 0x9e7f008
p[1] no_value: address is 0x9e7f009
p[2] no_value: address is 0x9e7f00a
p[3] no_value: address is 0x9e7f00b
p[4] no_value: address is 0x9e7f00c
由程序知,存在栈中的局部指针变量指向堆中一大小为N * sizeof(char)字节的内存,并没有对这段内存进行初始化(指定存的字符)。从运行结果来看,使用malloc函数分配的内存的初始值(p[i])不定(当使用malloc为int、float分配空间时,p[i]的值为0,0.000000),为相应数据分配的地址连续(char型数据占一个字节,内存以一个存储单元(字节)编址)。
当把程序中memset(p, '1', N * sizeof(char))前的注释符去掉时,再次编译运行的结果为:
gcc -Wall malloc_1.c -o malloc_1 && ./malloc_1
p[0] no_value:1 address is 0x852b008
p[1] no_value:1 address is 0x852b009
p[2] no_value:1 address is 0x852b00a
p[3] no_value:1 address is 0x852b00b
p[4] no_value:1 address is 0x852b00c
可见,为了避免用malloc函数申请的空间未初始化就使用后带来的错误,可用memset()函数对每个字节初始化。
注意:程序中char类型数据只占据一个字节的内存,故数据输出与初始化值一样。倘若别的数据类型(如int)占多个字节(4个),则要按照二进制高地位排列得到整个int的值
例memset_1.c
将malloc_1.c中char用int换掉,并把memset(p, '1', N * sizeof(char))中的参数'1'改为1(参数char要换位int),将printf("p[%d] no_value:%c\taddress is %p\n", i, p[i], &p[i]);中的%c换位%x.
编译运行:
gcc -Wall memset_1.c -o memset_1 && ./memset_1
p[0] no_value:1010101 address is 0x90b8008
p[1] no_value:1010101 address is 0x90b800c
p[2] no_value:1010101 address is 0x90b8010
p[3] no_value:1010101 address is 0x90b8014
p[4] no_value:1010101 address is 0x90b8018
仔细观察一下输出结果,将会发现依memset函数功能描述:将值以二进制的形式存储在每一个字节中,而一个整形数据占4个字节,所以当输出一个整形数据时,它就会把4个字节的数据由高到低排列在一起形成32位,从而得到一个整形数据的值。后面的地址表明,一个整形数据占4个字节(以字节为单位编址)
在需要的内存不需要时,一定要及时使用free(p)的把所申请的堆空间释放掉,因为堆的空间需要程序员(编写程序的人~·~)手动释放,若当以前分配的一片内存不再需要使用或无法访问时,但是却并没有释放它,那么对于该进程来说,会因此导致总可用内存的减少,这时就出现了内存泄漏。
符堆空间数据存储图:
当然,我觉得这不仅表示数据在堆中的存储,数据存储都是以字节为单位再根据数据变量类型占据的字节数以向高或者向低地址存储的。