动态内存分配 [李园7舍_404]

上次笔记C语言的时候笔记到关于C语言的不同数据变量类型的存储方式。包括变量的存储位置,什么时候为其分配内存,未被初始化时的初始值,作用域,生存期等话题。并留了一个关于“堆”的动态内存分配问题。趁课余时间聊聊动态内存分配及使用动态内存分配时需要注意的问题。


为什么要动态内存分配

  • 栈空间大小有限
这主要是体现在较大的程序上面。动态局部变量是存储在栈上的,而且栈的空间有限,太大的程序就需要额外的空间来存储变量。这个时候就可以向堆索取动态的内存,即动态内存分配。
  • 申请一个与数据(如某文件)所需内存大小相宜的内存空间,避免空间的浪费,同时避免空间不够。
在不知道数据有多大时,可以用一个较大的下标来定义 一个数组来存储相应的数据,这样一来有可能这个下标值不够大,造成数组上越界,也有可能下标值过大,造成内存浪费(在相应的程序块结束后才自动释放)。
  • 可以在不用堆内存空间时手动释放相应的空间(存在栈上面的数据要在相应的程序块运行结束时才会被释放)
 
怎么进行动态内存分配
进行动态分配的对象一般是指针(C语言),可以使用malloc、calloc、realloc函数来申请内存(堆)空间。当然到内存不需要的时刻需要手动使用free函数来释放相应的空间。以免造成内存泄露
1.malloc、calloc、realloc区别

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)的把所申请的堆空间释放掉,因为堆的空间需要程序员(编写程序的人~·~)手动释放,若当以前分配的一片内存不再需要使用或无法访问时,但是却并没有释放它,那么对于该进程来说,会因此导致总可用内存的减少,这时就出现了内存泄漏。

 

符堆空间数据存储图:

当然,我觉得这不仅表示数据在堆中的存储,数据存储都是以字节为单位再根据数据变量类型占据的字节数以向高或者向低地址存储的。

你可能感兴趣的:(碚大)