动态内存分配

1.常用函数

笔记

代码malloc(掌握)

#include<stdio.h>
#include<stdlib.h>
int main()
{
    //1.利用malloc函数申请一片连续的空间
    //需求:申请一片空间,要存储100个int类型的整数
    //返回这片空间的首地址,,所以可以用指针来记录
    int*p= malloc(100 * sizeof(int));//得考虑通用性
    //打印一下p所记录的内存地址
    printf("%p\n", p);

    //此时用malloc这个函数,申请出来的空间,他里面是没有默认值的,如果说在这个下面强行获取,这些数据是乱七八糟的数据

    printf("%d\n", *p);//这个是错误的写法
    //在后面直接*p解引用就可以了,现在是100个int,此刻打印出的就是第一个int的值
    //所以使用之前要先赋值

    //2.赋值
    for (int i = 0; i < 100; i++)
    {
        //第一种赋值
        //*(p + i) = (i + 1) * 10;//等号右边相当于现在要赋的100个整数,10 20 30 40 50 60。。。
        //等号左边:第一次循环时,i是0,这个指针就不做任何偏移,再进行解引用,相当于把10赋值给了上面100个整数当中的第一个
        //第二次循环时,i=1,相当于就是把这个指针往后偏移了一个单位,那么一个单位是跟指针的 步长有关系的,
        // 往后一步4个字节,那指的就是第二个int类型的整数了,所以就把20赋值给了第二个整数
        //循环之后就表示100个整数全部已经复制完毕


        //第二种赋值
        //可以仿照数组的写法,上面这个空间也是连续的,内存的结构跟数组几乎一模一样,
        p[i] = (i + 1) * 10;
        //其实数组这种方式在底层,在真正运行时,他会把这个p和i进行解析,最终还是解析成p+i,再把这个整体进行解引用
        //所以在底层其实就是拿着p也就是那个指针往后偏移了几个单位,然后再解引用
        //i[p]这样也是可以的,就是以这种方括号的形式来进行书写的话,他的底层会进行解析的,
        // 把外面的和里面的相加,把这个整体再进行解引用

    }

    //3.遍历(把100个整数全部打印出来)
    for (int i = 0; i < 100; i++)
    {
        //法一//printf("%d ", *(p + i));
        printf("%d ", p[i]);//法2
    }



    return 0;
}

Calloc(了解)

realloc(了解)

#include<stdio.h>
#include<stdlib.h>
int main()
{
    //1.利用malloc函数申请一片连续的空间
    //需求:申请一片空间,要存储100个int类型的整数
    //返回这片空间的首地址,,所以可以用指针来记录
    int*p= malloc(100 * sizeof(int));//得考虑通用性
    //int* p = calloc(10, sizeof(int));


    //打印一下p所记录的内存地址
    //printf("%p\n", p);


    //2.赋值
    for (int i = 0; i < 10; i++)
    {
        //第一种赋值
        //*(p + i) = (i + 1) * 10;//等号右边相当于现在要赋的100个整数,10 20 30 40 50 60。。。
        //第二种赋值
        p[i] = (i + 1) * 10;
    }



    //4.扩容,20个int类型的整数
    int*pp=realloc(p, 20 * sizeof(int));
    //第一个参数,就是把p,也就是原来空间的这个指针给他传递过去,后面就是字节的多少
    //此时,这个函数也会返回一个内存地址,而且原来空间里面的数据他不会丢,他会把原来的数据一起的拷贝到新的空间当中


    //3.遍历(把100个整数全部打印出来)
    for (int i = 0; i < 20; i++)
    {
        //printf("%d ", *(p + i));
        printf("%d ", p[i]);
    }

    //首先一开始是申请了一片连续的空间,存储10个int类型的整数,下面呢可以给他赋值10个int类型的整数,
    // 复制完之后,我把这个空间进行扩容,新的空间要存储20个int类型的数据,那么在扩容的同时,他会把原本的10个整数赋值过去
    //所以说下面我们在遍历的时候,会发现前面10个还是原来的  ,后面的没有赋值,所以就是一些乱七八糟的数据

    return 0;

}

free(掌握)

不管是用哪种方式去申请内存的空间,那么这个空间如果说你不释放的话,空间在内存当中永远存在,如果说这样的空间越来越多,总有一天内存会放不下,所以说当这个空间不需要了,一定要随手把他给释放掉

2.malloc函数的细节点

1,malloc创建空间的单位是字节

2,malloc返回的是void类型的指针,没有步长的概念,也无法获取空间中的数据,需要强转3,malloc返回的仅仅是首地址,没有总大小,最好定义一个变量记录总大小 指的是元素的个数

4,malloc申请的空间不会自动消失,如果不能正确释放,会导致内存泄露

5,malloc申请的空间过多,会产生虚拟内存

6,malloc申请的空间没有初始化值,需要先赋值才能使用

7,free释放完空间之后,空间中数据叫做脏数据,可能被清空,可能被修改为其他值

8,calloc就是在malloc的基础上多一个初始化的动作

9,realloc修改之后的空间,地址值有可能发生变化,也有可能不会改变,但是原本的数据不会丢失10,realloc修改之后,无需释放原来的空间,函数底层会进行处理

#include <stdio.h>
#include<stdlib.h>
void method(int* p, int size);

int main()
{
    //5,malloc申请的空间过多,会产生虚拟内存
    // 
    // 虚拟:假的
    // 当申请的空间过多,因为每一个内存空间不会在刚申请的时候就立马使用
    // 所以c语言并不会立马就在内存中去开辟空间,而是什么时候存储数据了,才会真正的分配空间
    // 目的:为了提高内存的使用效率
    //表示单次申请空间的字节大小(1G)
    int number = 1024 * 1024 * 1024;

    //利用循环不断地申请空间
    //malloc   申请空间
    //如果申请空间成功,返回这个空间的首地址
    //如果申请失败,返回NULL
    int count=0;
    while (1)
    {
        int *p = malloc(number);//每次申请一个G的内存空间,用指针来记录
        count++;
        if (p == NULL)
        {
            printf("申请失败");
            break;
        }
        printf("内存%d申请成功%p\n", count, p);
    }

    return 0;

}
#include<stdio.h>
#include<stdlib.h>
int main()
{
    //1.申请一片连续的空间存储10个int类型的整数
    int* p1 = malloc(10 * sizeof(int));
    int size = 10;
    printf("修改之前的内存地址为:%p\n", p1);
    //给这片空间赋值
    for (int i = 0; i < size; i++)
    {
        *(p1 + i) = (i + 1) * 10;
    }

    //3.修改大小
    int* p2 = realloc(p1, 20 * sizeof(int));//p1,相当修改的就是这个空间 malloc(10 * sizeof(int));
    //这个空间要修改多大,即 20 * sizeof(int)表示修改之后要存储20个int 类型的整数
    printf("修改之后的内存地址为:%p\n", p2);
    size =20;

    //realloc修改之后,无须释放原来的空间,函数底层会进行处理
    //如果内存地址没变,底层在原来空间的后面接着申请的
    //如果内存地址变了,申请一个新的大的空间,把原来的数据拷贝到新的空间当中,再把原来的空间给free掉





    //4.遍历
    printf("遍历空间中的数据为:\n");
    for (int i = 0; i < size; i++)
    {
        printf("%d ", *(p2 + i));//用p2进行遍历
    }
    printf("\n");

    return 0;

}

3.c语言的内存结构

代码区:当程序在运行的时候,他会先把代码 加载到代码区,临时存储

栈:函数在调用的时候,会进栈执行,当函数里面所有的代码全部执行完了之后,就会从栈里面出去

而函数 里面定义的变量或者是数组其实也是在栈里面的,

静态区:但是如果说变量前面加上了static,或者把变量的定义写在函数外面了,这个时候就不在栈里面了,变成全局变量,

未初始化静态区:那么此时变量还没有赋值,就会在未初始化静态区

初始化静态区:如果说已经赋值了,就会在初始化静态区

常量区:如果说指针加双引号的形式定义的字符串,那么字符串的底层其实就是字符数组,而这个数组是放在常量区的,一旦放在这,就会有2个细节:一个是里面的内容不能修改,一个是会有一个复用机制

堆:只有用malloc,calloc,realloc这样3个函数申请出来的空间,才会在堆里面

4.变量数组在内存中的运行情况

首先,程序要运行的时候,他会把所有的代码都临时加载到代码区当中,进行存储,在代码区当中,他只是临时存储,不会去运行里面的代码,当程序开始运行的时候,编译器就会从上往下依次去检查代码,,看到main函数就会自动去调用他,这个时候main函数就会加载到栈里面,开始执行里面代码,而在main函数中,我一开始定义了2个变量,a和b,那么就在main函数里面开辟了2个小空间,分别存储数据10那么如果说在main函数当中下面的代码里面,要用到a和b,就会找这2个变量里面对应的值,变量存储的是什么,就使用什么,还有一个数组,所以在左边的main函数中,就定义了一个长度为3的数组,里面分别存储元素1,2,3,那么这3 个元素在内存当中,是连续不断的,现在在整个内存当中,是没有一个变量叫做arr的,arr现在表示的是数组这个整体,是这个大的空间,这也是为什么用sizeof去测量arr时会获取到整个数组占用多少字节的原因,只不过arr在参与计算的时候,他会退化为指向第一个元素的指针,当main函数里面的所有代码全部执行完毕了,整个时候main函数就会从栈里面出去,一旦函数从栈里面出去了,那么函数里面定义的所有的变量还有数组也会随之消失,函数里面所有变量和数组的生命周期都是跟函数有关的

5.全局变量和static变量在内存中运行情况

此时是把变量a定义在函数外面,此时这个变量A就叫做全局变量

在所有函数,都可以使用这个A,

6.字符串在内存中的运行情况

在main函数当中,第一行是用指指针加双引号的形式,去定义的字符串,只有这种形式定义的字符串,他才会把底层的字符数组放在常量区当中,如果表示用这种方式,底层的字符数组是放在栈里面,那么字符串里面的内容就可以随意发生修改,

所以说在一开始使用指针加双引号的时候,在一开始,会去检查常量区里面有没有abc,如果说没有,才会创建一个新的,然后把他的地址值赋值给main函数里面的指针str,所以说在下面我通过str去打印字符串中所有内容时,就会打印到这里的abc

可以创建一个新的字符串,叫做aaa,在创建aaa的时候,编译器也会在常量区中检查有没有aaa,如果说有,就不会创建新的了,会进行复用,但是现在没有,所以才会创建新的数组aaa,并把他的地址值赋值给左边的str,str所记录的地址就会发生变动,此时再次打印str就是新的aaa,

7.malloc函数在内存中的运行情况

如果说要完整的遍历堆里面的这个空间,最好就要用一个单独的变量,去记录他的长度才可以

用我们以前的方式定义的数组是在栈里面的,而用melloc定义的空间,是在堆里面的,而堆里面的空间比栈要大的多,如果说数组中的内容比较多,这个时候放到栈里,不太合适,

如果说你这个数组是在栈里面的,那么他的生命周期是跟函数相关的,函数一旦执行完毕了,里面的数组也会随之消失,如果在其他函数里面还想要用下面这个数组,其实是用不了的,因为函数都消失了,函数里面的东西也就没了,而使用melloc,则是在堆里面,只要不要free,那么他永远存在,一直到程序 的结束才消失

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值