美文网首页
堆区空间申请

堆区空间申请

作者: 晓晓桑 | 来源:发表于2020-04-13 22:44 被阅读0次

栈、堆、全局区、字符常量区、代码区
栈区:由操作系统申请,在变量生命周期结束后由操作系统释放。

堆区:由我们申请,由我们自己释放

malloc(size_t __size)

int main(void) {

    //申请4字节的空间,类型是int的地址,强制类型转换。
    //申请完的地址的首地址就是p
    int *p = (int *) malloc(4);
    return 0;
}
size_t: unaigned int
  • 32位编译器:unaigned int:sizeof =4
  • 64位编译器:long unaigned int:sizeof =8
malloc传无符号,(int *) malloc(4)中4默认是int类型的,c语言会转换成无符号的,如果写成(int *) malloc(4u)就直接是无符号的。无符号就是全是整数,跟有符号的取值范围不一样哦。
malloc申请空间
  • malloc(不是太大的数):
  • malloc(0):malloc得到空间首地址,但是可用地址的长度为0,意思就是地址不能用。
  • malloc(超出极限):申请的空间大小超过操作系统所给的内存大小,则返回null,地址为0
malloc空间赋值
int main(void) {
    int *p = malloc(40);
    
    //申请的40个字节,能装10个int数据
    for (int i = 0; i < 10; ++i) {
        //给指针赋值
        p[i] = i;
    }
    //赋值后,读取
    for (int j = 0; j < 10; ++j) {
        printf("%d,", p[j]);//0,1,2,3,4,5,6,7,8,9,
    }
    return 0;
}

memset赋值

int main(void) {
    int *p = malloc(40);
    //内存赋值方法
    //第一个参数:起始地址,
    //第二个参数:给起始地址设置的值
    //第二个参数:一共设置多少个字节
    memset(p, 0, 40);
    //赋值后,读取
    for (int j = 0; j < 10; ++j) {
        printf("%d,", p[j]);//0,0,0,0,0,0,0,0,0,0,
    }
    return 0;
}
memset()是按字节赋值的

是把范围内的所有字节都设置成值1(下面的代码里的),而输出是int类型的,int类型占四个字节,所以输出四个值都是1的字节的值,即16843009

  • 16843009是怎么得出来的呢?


    image.png
int main(void) {
    int *p = malloc(40);
    //内存赋值方法
    //第一个参数:起始地址,
    //第二个参数:给起始地址设置的值
    //第二个参数:一共设置多少个字节
    memset(p, 1, 40);
    //赋值后,读取
    for (int j = 0; j < 10; ++j) {
        printf("%d,", p[j]);//16843009,16843009,16843009,16843009,16843009,16843009,16843009,16843009,16843009,16843009,
    }
    return 0;
}

所以如果赋值成0,才能使用memse()

malloc()注意点

1.malloc申请的字节数一定要对应类型,比如申请int型,那么一定要大于4,double的话就要大于8,因为他们类型一次要操作这些个字节,少了不行,多了可以不用。
2.malloc申请内存之后,就不要再malloc了。但是如果malloc的地址记录一下,然后再指向新的地址,就不会丢失空间了。

int main(void) {
    int *p = malloc(40);

    //p重新指向另一块地址,那么原来的地址就不用了,这样会造成内存泄漏
    p = malloc(4);

    return 0;
}
int main(void) {
    int *p = malloc(40);
    int *p1 = p;
    //专业就不会丢失空间了.或者原来的空间不使用直接调用free(p)
    p = malloc(4);
    return 0;
}
free(p)释放指针
  • free(p)之后,地址还是那个地址。
  • free(p)之后,把p赋值为NULL:p=NULL
int main(void) {
    int *p = malloc(40);
    *p = 12;
    printf("%p,%d\n", p, *p);
    free(p);
    p = NULL;
    if (NULL == p) {
        //p是空指针 0==null=空
    }
    return 0;
}
  • 空指针: 0==null=空
  • 野指针:指向一个地址,但是不能访问。有两种情况:
    1.free(p)之后,没把p设置成null,则指针是野指针。
    2.int *p;指针初始化,但是没有赋值,这个也是野指针。
  • 不能重复释放一个指针


    image.png
  • free()不能释放占空间


    image.png
  • free()一定要释放头指针


    image.png
  • malloc的空间在程序结束的时候,系统会释放所有malloc的内存,那么为什么还有free()呢?
    1.服务器不会频繁重启;
    2.使用软件也不会短时间频繁重启。
malloc一维数组
  • 基本数据类型的空间的申请与使用


    image.png
  • 数组的指针申请
    一维数组栈内存:比如int a[5],是申请一个长度为5的内存,每个内存为四字节,这内存是堆内存。
    而malloc(n)是:申请一个长度为n字节的内存,然后根据类型的字节数划分存储单位,比如int类型,则以4字节为一个单位存储。malloc申请的内存栈内存。
int main(void) {
    //申请的连续的空间
    //申请出来的地址就是内存的头地址
    int *p = malloc(sizeof(int) * 5);//sizeof(int)*5=20
    int a[5];
    int *p2 = &a[0];//将指针的首地址指向数组的首地址
    return 0;
}
int main(void) {
    //申请的连续的空间
    int *p = malloc(sizeof(int) * 5);//sizeof(int)*5=20
    memset(p, 0, sizeof(int) * 5);//memset只能设置成0
    return 0;
}
  • *(p+i)==p[i];
    malloc的数组与int a[3]这种栈区数组的区别?
    1.定义上不同,但是使用上一样。
    2.malloc的数组可以这运行过程中指定任意大小,即动态数组,动态分配空间。
    int a[3]就不一样了,个数在定义的时候确定,以后就不能改了。
int main(void) {
    unsigned int a;
    int *p;
    scanf("%u", &a);
    p = malloc(a);
    free(p);
    return 0;
}
malloc地址转化成一维数组指针/二维数组指针

malloc一维数组指针
(p+n)=*p[n]

int main(void) {
    int a[5];
    //栈区一维数组指针的写法
    int(*p1)[5] = &a;

    //对比上面的写法
    //malloc一维数组指针
    int  (*p)[5] = (int (*)[5]) malloc(sizeof(int) * 5);
    *(*p + 0) = 1;
    *(*p + 1) = 2;
    *(*p + 2) = 3;
    *(*p + 3) = 4;
    *(*p + 4) = 5;


    //遍历malloc的数组数组a的元素
    for (int i = 0; i < 5; ++i) {
        printf("%d,", (*p)[i]);//1,2,3,4,5,
    }
    
    free(p);
    return 0;
}

malloc二维数组指针

int main(void) {
    int a[2][3];
    //栈区二维数组指针的写法
    int(*p1)[2][3] = &a;// *p1 == a;

    //对比上面的写法
    //malloc二维数组指针:
    int  (*p)[2][3] = (int (*)[2][3]) malloc(sizeof(int) * 2 * 3);
    //二维指针数组p赋值
    (*p)[0][0] = 1;
    (*p)[0][1] = 2;
    (*p)[0][2] = 3;
    (*p)[1][0] = 5;
    (*p)[1][1] = 8;
    (*p)[1][2] = 10;
    //遍历malloc的数组数组a的元素
    for (int i = 0; i < 2; ++i) {
        for (int j = 0; j <3 ; ++j) {
            printf("%d,", (*p)[i][j]);//1,2,3,5,8,10,
        }
    }
    free(p);
    return 0;
}
calloc realloc
  • calloc:申请一个数组空间,空间也是连续的
  • calloc(5,4);//申请了一个 5个元素每个元素4个字节的空间
  • calloc 申请出来地址他给初始化成0了,而malloc不给初始化,字节用memset()初始化
int main(void) {
    //1:数组元素个数,2:每个元素的字节数
    int *a = calloc(5, 4);//申请了一个 5个元素每个元素4个字节的空间

    for (int i = 0; i < 5; ++i) {
        printf("%d,", a[i]);//0,0,0,0,0,
        //calloc 申请出来地址他给初始化成0了,而malloc不给初始化,字节用memset()初始化。
    }

    free(a);
    return 0;
}

realloc:重新给malloc或者calloc分配空间

int main(void) {
    int *p = malloc(12);

    //1:分配空间的首地址,2:重新分配空间的大小
    int *p1 = realloc(p, 20);//正常情况p1 和p的地址是一样的,不正常的看下面的注意
    //如果重新分配的空间,系统分配不足,会返回null

    free(p);
    return 0;
}

注意:
如果malloc或者calloc原来的空间是10个字节,让realloc重新1000个字节:

  • 如果整个内存条上都没有足够1000字节的空间,就返回null。
  • 如果其他的内存碎片或者内存上有1000字节的空间,那么指针地址就会变成这个内存地址,然后把原来的值放到对应的地方。
calloc 和malloc的选择
  • calloc申请申请数组好用,直接calloc(5,4),calloc申请好数组直接初始化成0,malloc得用memset设置成0。
  • 其他的数据结果,比如链表,树,图,一次申请sizeof(节点),这些用malloc更合适
  • 效率:calloc会初始化内存,所以申请的内存很多的时候,效率会低一点点,毕竟有时候不需要初始化,malloc这就效率高一些。其实微乎其微啦~~~~

*在C中的四种作用

  • 声明的时候有:表示指针变量 intp
  • *地址:表示地址操作符 *p
  • 数字*数字 :乘法
  • 注释:/* */

相关文章

  • 堆区空间申请

    栈、堆、全局区、字符常量区、代码区栈区:由操作系统申请,在变量生命周期结束后由操作系统释放。 堆区:由我们申请,由...

  • C++之new和delete

    概述 new是从堆区申请空间(作用等价于c语言:malloc calloc realloc) delete是释放堆...

  • 4.2.5浅拷贝和深拷贝

    浅拷贝:简单的赋值拷贝操作深拷贝:在堆区重新申请空间,进行拷贝操作

  • C语言基础(3)内存分配malloc与free

    1、申请内存的区域 栈区、堆区、全局区、字符常量区、代码区 定义的变量数组等都是在栈区申请,栈区的内存由系统申请和...

  • 1222学习总结

    栈: 堆: malloc()和free()要成对出现(即申请的堆空间要及时释放),防止出现堆空间不足的现象 常见段...

  • 栈/堆/全局区/常量区/代码区

    来源:iOS程序中的内存分配 栈区堆区全局区 - 简书 栈:存储局部变量 堆:允许程序员手动在堆区申请指定的连续的...

  • C++ 从入门到放弃 (Day-05)

    堆空间 ◼ 在程序运行过程,为了能够自由控制内存的生命周期、大小,会经常使用堆空间的内存◼ 堆空间的申请\释放m...

  • NDK之C++静态开辟内存与动态开辟内存

    静态开辟内存 申请的是栈区的,动态开辟 申请的是堆区的 ,这么理解对嘛 对的relloc 返回 null 后,原来...

  • 汇编分析String Array 底层

    swift系列课程 内存地址从低到高由↓代码区↓常量区↓全局区(数据段)↓堆空间↓栈空间↓动态库 string d...

  • 面向对象程序设计和构造方法

    一、内存分区 栈区:方法的工作空间,数据结构是后进先出堆区:二叉树(稳定),存放对象,分为属性空间和方法空间方法区...

网友评论

      本文标题:堆区空间申请

      本文链接:https://www.haomeiwen.com/subject/sexaphtx.html