c语言动态内存分配

来源:互联网 发布:北京达内java 编辑:程序博客网 时间:2024/05/17 07:33

数组的元素存储于内存连续位置上。当一个数组被声明时,它所需要的内存在编译时期就会被分配。但是,你也可以使用动态内存分配在运行时为他分配内存。我们如何使用动态内存分配以及怎样进行动态内存分配?

c语言中的内存区域

C语言中内存分为四区:

  • 栈区:由编译器自动分配释放,存放函数的参数,局部变量的值。
  • 堆区:由程序员分配释放,即动态内存的申请和释放。
  • 全局区:全局变量和静态变量是放在一块的。初始化的全局变量和未静态变量在同一个区域,未初始化的全局变量和静态变量在相邻的另外一个区域
  • 代码区:存放函数体的二进制代码。
  • 文字常量区:常量字符串就是放在这里,程序结束后由系统释放。

这里需要注意的是堆和栈的最主要的区别,栈区是编译期自动释放,无需手动释放,而堆区需要程序员手动释放


内存分配在堆栈中的区别

内存分配分为两种:

  • 静态分配。
    静态内存分配,分配内存大小的是固定,问题:1.很容易超出栈内存的最大值 2.为了防止内存不够用会开辟更多的内存,容易浪费内存。
    int a[1024 * 1024 * 10];

比如上面的代码就是静态内存分配。

  • 动态分配。

    • 当一个数组被声明时,它所需要的内存在编译时期就被分配。你也可以使用动态内存分配在运行时为它分配内存。

    • 动态内存分配,在程序运行过程中,动态指定需要使用的内存大小,手动释放,释放之后这些内存还可以被重新使用。


malloc 和 free

c函数库中提供了两个函数,malloc和free。分别用来执行动态内存的分配和释放。

这两个函数都在stdlib.h中声明。

void *malloc( size_t size);void *free(void * pointer);

使用时需要导入

#include <stdlib.h>

malloc

  • malloc 参数就是分配的内存字节数,返回的是内存块起始的指针。
  • malloc分配一块连续的内存,并不会分开两块或者多块内存的区域。
  • 如果内存池是空的,内存无法满足你的请求,就会返回一个NULL指针

free

free参数要么为NULL,要么是malloc,calloc,或者realloc返回的值。
向free传递一个NULL,不会产生效果。


代码示例:

1.创建一个静态数组。

#define _CRT_SECURE_NO_WARNINGS#include <stdio.h>#include <stdlib.h>#include <Windows.h>//栈内存void stackFun(){    int a[1024];    //栈内存自动释放}//堆内存void heapFun(){    //40M内存    //字节    //void *任意类型的指针    int* p = malloc(1024 * 1024 * 10 * sizeof(int));    //释放    free(p);}void main(){        //在堆内存上,分配40M的内存    while (1){        Sleep(1000);            stackFun();    }    getchar();}

如代码中,我们定义一个while循环,分别对堆内存和栈内存进行分配。我们会发现堆内存会不断上涨。如果使用free,就不会出现这种情况。而栈内存不会增长。

free(p);

2.创建一个数组,动态指定数组的大小

#define _CRT_SECURE_NO_WARNINGS#include <stdio.h>#include <stdlib.h>#include <Windows.h>//创建一个数组,动态指定数组的大小void main(){    //静态内存分配创建数组,数组的大小是固定的    //int i = 10;    //int a[i];    int len;    printf("输入数组的长度:");    scanf("%d",&len);    //开辟内存,大小len*4字节    int* p = malloc(len * sizeof(int));    //p是数组的首地址,p就是数组的名称    //给数组元素赋值(使用这一块刚刚开辟出来的内存区域)    int i = 0;    for (; i < len - 1; i++){        p[i] = rand() % 100;        printf("%d,%#x\n", p[i], &p[i]);    }    //手动释放内存    free(p);    getchar();}

realloc和calloc

realloc 和 calloc的原型如下:

void *calloc( size_t num_elements,size_t element_size);void *realloc(void *ptr, size_t new_size);

calloc

用于分配内存。malloc和calloc之间的主要区别就是calloc在返回指向内存的指针之前把它初始化为0。

另一个较小的区别就是,calloc的参数包括所需元素的数量和每个元素的字节数。根据这些值,他就能计算出总共需要分配的内存。

realloc

用于修改一个原先已经分配内存的大小,可以使一块内存扩大或者缩小。
重新分配内存分为两种情况:
- 缩小
- 扩大(连续的)

  1. 如果当前内存段后面有需要的内存空间,直接扩展这段内存空间,realloc返回原指针。
  2. 如果当前内存段后面的空闲字节不够,那么就使用堆中的第一个能够满足这一要求的内存块,将目前的数据复制到新的位置,并将原来的数据库释放掉,返回新的内存地址。
  3. 如果申请失败,返回NULL,原来的指针仍然有效。
  4. realloc参数第一个为NULL,那么它的行为就和malloc一样。

代码演示

#define _CRT_SECURE_NO_WARNINGS#include <stdio.h>#include <stdlib.h>#include <Windows.h>//realloc 重新分配内存void main(){    int len;    printf("第一次输入数组的长度:");    scanf("%d", &len);    //int* p = malloc(len * sizeof(int));       int* p = calloc(len, sizeof(int));    int i = 0;    for (; i < len; i++){        p[i] = rand() % 100;        printf("%d,%#x\n", p[i], &p[i]);    }    int addLen;    printf("输入数组增加的长度:");    scanf("%d", &addLen);    //内存不够用,扩大刚刚分配的内存空间    //1.原来内存的指针 2.内存扩大之后的总大小            int* p2 = realloc(p, sizeof(int) * (len + addLen));    if (p2 == NULL){        printf("重新分配失败,世界那么大,容不下我。。。");    }    //重新赋值    i = 0;    printf("--------------------------\n");    for (; i < len + addLen; i++){        p2[i] = rand() % 200;        printf("%d,%#x\n", p2[i], &p2[i]);    }    //手动释放内存    /**if (p != NULL){        free(p);        p = NULL;    }       if (p2 != NULL){        free(p2);        p2 = NULL;    }    **/    getchar();    system("pause");}

结果为:
这里写图片描述

从图中可以看到数组首地址是不变的。


内存分配注意的几个细节

  • 1.不能多次释放
  • 2.释放完之后(指针仍然有值),给指针置NULL,标志释放完成
#define _CRT_SECURE_NO_WARNINGS#include <stdio.h>#include <stdlib.h>#include <Windows.h>void main(){    int len;    printf("输入数组的长度:");    scanf("%d", &len);    int* p = malloc(len * sizeof(int));         int i = 0;    for (; i < len; i++){        p[i] = rand() % 100;        printf("%d,%#x\n", p[i], &p[i]);    }    if (p != NULL){        free(p);        p = NULL;    }    getchar();}
  • 3.内存泄露(p重新赋值之后,再free,并没有真正释放内存)
void main(){    //40M    int* p1 = malloc(1024 * 1024 * 10 * sizeof(int));    //1处。    //free(p1);    //p1 = NULL;    printf("%#x\n",p1);    //80M    p1 = malloc(1024 * 1024 * 10 * sizeof(int) * 2);    free(p1);    p1 = NULL;    getchar();}

如果1处注释掉。我们来看一下。windows任务管理器。第二个参数为内存占用。
这里写图片描述
如果把注释打开:
这里写图片描述

相差40m。

原创粉丝点击