美文网首页
内存布局和堆的分配释放

内存布局和堆的分配释放

作者: 木鱼_cc | 来源:发表于2018-05-22 17:38 被阅读0次

    1.代码区

    代码区code,程序被操作系统加载到内存的时候,所有的可执行代码都加载到代码区,也叫代码段,这块内存是不可以在运行期间修改的。
    代码区中所有的内容在程序加载到内存的时候就确定了,运行期间不可以修改,只可以执行。

    2.静态区

    静态区是程序加载到内存的时候就确定了,程序退出的时候从内存消失。所有的全局变量和静态变量在程序运行期间都占用内存。
    所有的全局变量以及程序中的静态变量和常量(const)都存储到静态区

    3.栈区

    stack是一种先进后出的内存结构,所有的自动变量,函数的形参,函数的返回值都是由编译器自动放出栈中,当一个自动变量超出其作用域时,自动从栈中弹出。
    不同的系统栈的大小是不一样的,即使相同的系统,栈的大小也是不一样的,windows程序在编译的时候就可以指定栈的大小,linux栈的大小是可以通过环境变量设置的。

    3.1静态区、代码区、栈区

    静态区和代码区的大小受到物理内存大小的限制
    栈区的大小一般很小,单位一般是k,所以栈中不能有太多变量


    内存说明.png

    4.堆区

    堆heap和栈一样,也是一种在程序运行过程中可以随时修改的内存区域,但没有栈那样先进后出的顺序。
    堆是一个大容器,它的容量要远远大于栈,但是在C语言中,堆内存空间的申请和释放需要手动通过代码来完成。

    1.png 2.png

    5.堆的分配和释放

    1.malloc

    void * malloc(size_t _Size);

    malloc函数在堆中分配参数_Size指定大小的内存,单位:字节,函数返回void*指针

    2.free

    void free(void *p);

    free负责在堆中释放malloc分配的内存。参数p为malloc返回的堆中的内存地址

    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
        char *p;
        p = malloc(100);//在堆中分配了一个100字节的内存
        //p指向了堆的内存首地址
        //变量p在哪里?p在栈里面,但p的值是堆地址编号
       free(p);
    //为什么要写free?因为p可以自动销毁,但是malloc产生的堆内存并不会自动销毁,所以要free函数清理
    
    
      //在堆中分配一个int
        int *p1 = (int*)malloc(sizeof(int));
       *p1 = 0;//在堆中的int的值设置为0
        printf("%d\n",*p1);
        
        free(p1);
        p1 = NULL;//避免野指针
      //在堆中分配一个int[10]
       int *p2 = malloc(sizeof(int) * 10);
       memset(p2,0,sizeof(int)*10);
       for(int i = 0; i<10;i++{
          printf("%d\n",p2[i]);
       }
       free(p2);
        p2 = NULL;//避免野指针
       /*
        *堆内存有一个最小单位,叫内存页
        *一个内存页的大小也不是固定的,在测试的机器里面是4k为一个单位变化的
        *当我们要堆里面一个内存的时候,总是4k为一个单位
        */
    
       return 0;
    }
    

    内存分配例子1

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    char *test()
    {
        char *p = malloc(100);//在堆中分配了100个char,假设堆地址为0x123
        strcpy(p,"hello");
        //free(p);//p就成了野指针
        return p;
    
    char *test1()//错误的代码
    {
           (auto) char p[] = "hello";
           return p;
    }
    
    char *test2()
    {//不要通过函数的返回值返回一个自动变量的地址(返回堆地址可以!)
        (auto) char a = 'a';//栈!
         return &a;
    }
    
    char *test3()//正确
    {
        static char a = 'a';
        return &a;//返回一个静态变量的地址是有效的,因为静态变量的内存一直存在
    }
    
    const char *test4()//和const没有关系,都是栈!
    {
         char a[] = "hello world";
         return a;
    }
    
    const char *test5()//正确的,参数const表示常量,常量在静态区,静态区可读写,可以返回!
    {
         const char *s = "hello world";
         return s;
    }
    
    const char *test6()
    {
          return "abc";//和test5道理一样
    }
    
    int main()
    {
        char *p2 = test2();//返回了野指针!
        *p2 = 'b';
    
        char *str1 = test1();//test1这个函数调用完成后,test1里面的自动变量地址都已经从栈中弹出了!
       //所以,str1是一个无效的地址
        
        char *str = test();
         str[0] = 'a';//str指向的不是代码区(代码区只读),而是指向的堆内存,所以可以改!
         printf("%s\n",str);
         free(str);
        return 0;
        
    
    }
    
    

    内存分配例子2

    void test2(char *s)//错误的,形参的值改变了,但实参没有改变,见图1!
    {//s是栈区的
        s = malloc(100);
        strcpy(s,"hello");
       //由于s在栈里面,test2这个函数执行完后,s就从栈中弹出了!
       //结果是malloc分配的内存再也没有机会free了!
    }
    
    void test3(char **s)//正确,见图2
    {
        *s = malloc(100);
        strcpy(*s,"hello");
    }
    
    char *test4()//这里没问题
    {
         char *s = malloc(100);
         strcpy(s,"hello");
         return s;
    }
    
    int main()
    {
    
       char *p = NULL;
       test3(&p);
       printf("%s\n",p);
       free(p);
       return 0;
      /*
      总结:如果通过一个函数内部给一个
    实参指针分配堆内存,一定是二级指针
    */
    
    
    
        char *p = NULL;
        test2(p);//p没有分配到堆的内存
        printf("%p\n",p);//p还是空指针
        return 0;
    
    }
    gcc调试
    gcc -o a a.c -g
    a
    gdb a
    where
    
    形参实参分析1.png

    p是实参,s是形参,p的值会给到s,所以s的值是NULL
    此时创建了堆内存空间,地址是0x123,那么s的值就是0x123
    往堆内保存"hello",也没有出错。
    但是p地址存值没有变化!!!!
    形参的值改变了,但是实参没有改变

    形参实参分析2.png

    用二级指针 **s 的话(如test3(&p))
    假设变量的地址是0x100,那么传到**s中,s的值是0x100
    malloc创建堆内存,地址是0x123
    *s的地址就是0x123,*s是什么,是0x100里面的值,所以*p=0x123

    内存分配例子3

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    const char *test1()//错误的
    {
        char a[] = "hello";
        return a;//a的值是一个自动变量的地址
    }
    const char *test2()//对的
    {
        char *a = "hello";
        return a;//a的值是一个常量的地址
    }
    const char *test3()//对的
    {
        static char a[] = "hello";
        return a;//a是静态变量
    }
    const char *test4()//对的
    {
        char a[] = malloc(100);
        strcpy(a,"hello");
        return a;
    }
    
    int main()
    {
       return 0;
    }
    

    相关文章

      网友评论

          本文标题:内存布局和堆的分配释放

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