美文网首页DevSupportC语言C语言
C语言内存管理一本道来

C语言内存管理一本道来

作者: PcDack | 来源:发表于2017-12-06 21:12 被阅读49次

    动态内存分配



    我们需要动态内存分配的原因

    • C语言中的一切操作都是基于内存的
    • 变量和数组都是内存的别名,如何分配这些内存由编译器在编译期间决定
      • 定义数组的时候必须指定数组长度
      • 而数组长度是编译器期就必须决定的

    ! 需求

    程序运行的过程中,可能需要使用一些额外的内存空间

    malloc和free

    动态内存分配.png
    • malloc所分配的是一块连续的内存,以字节为单位,并且不带任何的类型信息
    • free用与动态归还

    注意

    • malloc实际分配的内存可能会比请求的稍微多一点,但是不能依赖于编译器的这个行为
    • 当请求的动态内存无法满足时malloc返回NULL
    • 当free参数为NULL时候,函数直接返回

    calloc和realloc

    • void* calloc(size_t num,size_t size)(单元的数量,单元的大小)
    • void* realloc(void* pointer,size_t new_size)(指向之前已经分配的内存新的内存需要的内存)重置动态内存空间的大小)

    程序的三个要素

    后进先出

    • 栈是现代计算机程序最为重要的概念之一
    • 栈在程序中用于维护函数的调用上下文,没有栈就没有函数,没有局部变量
    • 栈保存了一个函数调用所需要的维护信息
      • 函数参数,函数返回地址
      • 局部变量
      • 函数调用上下文
    栈.png

    tips:不能将一个指针指向一个局部变量

    堆内存需要主动申请

    • 为什么有了栈还需要堆?
      • 栈上的数据在函数返回后就会被释放掉,无法传递到函数外部,如局部变量
    • 堆是程序中一块巨大的内存空间,可由程序自由支配
    • 堆中被程序申请使用的内存在程序主动释放前将一直有效
    • 系统对堆的管理方式
      • 空闲列表法,位图法,对象池法等等

    程序的静态存储区

    • 程序的静态存储区随着程序的运行而分配空间,直到程序运行结束
    • 在程序编译期间静态存储区的大小就已经确定
    • 程序的静态存储区主要用于保存程序中的全局变量和静态变量
    • 与栈和堆不同,静态存储区的信息最终会保存在可执行程序中

    小结

    • 栈,堆和静态存储区是C语言常设计的三个基本区
    • 栈区是主营帅帅函数的调用的使用
    • 堆区主要是用来内存的动态申请和归还
    • 静态存储区用于保存全局变量和静态变量

    程序的内存布局

    • 代码在可执行程序中的对应位置
      • File Header告诉系统则是个可执行的文件
      • data段用来存储有初始化的数据
      • bss段用来存放没有初始化的数据
      • text段是代码段

    程序文件的一般布局

    程序文件的一般布局.png

    程序的内存布局(运行的时候,由操作系统分配)

    内存布局.png

    各个段的作用

    • 堆栈段在程序运行后才正式存在,是程序运行的基础(最先建立的原因是因为活动进程)
    • .bss段存放的是未初始化的全局变量和静态变量
    • .text段存放的是程序中的可执行的代码
    • .dada段存放的是程序中已经初始化的全局变量和静态变量(必须要写到程序文件里面去)
    • .rodata段存放程序中的常量值,如字符串常量(只读数据段)

    .bss和.data段就是所谓的静态存储区

    小结

    • 静态存储区指的是.bss和.data
    • 只读区指的是.rodata
    • 局部变量所占用的空间为栈上空间
    • 动态空间为堆空间
    • 程序可执行代码存放与.text段

    头疼的野指针(了解野指针)

    • 野指针通常是因为指针变量中保存的值不是一个合法的内存地址而造成的
    • 野指针不是NULL,是一个指向不可用内存的指针
    • NULL指针不容易弄错,可以通过if来判断是否为NULL指针

    C语言中没有方法可以判断是否为野指针

    野指针的由来

    • 局部指针变量没有被初始化例子1
    • 使用已经释放后的指针例子2
    • 指针所指向的变量在指针之前被销毁例子3

    最佳示例 例子1

    #include <stdio.h>
    #include <string.h>
    
    struct Student
    {
        char* name;
        int number;
    };
    
    int main()
    {
        struct Student s;
        strcpy(s.name, "Delphi Tang"); // OOPS!
        s.number = 99;
        return 0;
    }
    

    最佳示例 例子2

    #include <stdio.h>
    #include <malloc.h>
    #include <string.h>
    
    void func(char* p)
    {
        printf("%s\n", p);
        free(p);
    }
    
    int main()
    {
        char* s = (char*)malloc(5);
        strcpy(s, "Delphi Tang");
        func(s);
        printf("%s\n", s); // OOPS!
        return 0;
    }
    
    

    最佳示例

    #include <stdio.h>
    char* func()
    {
        char p[] = "Delphi Tang";
        return p;
    }
    
    int main()
    {
        char* s = func();
        printf("%s\n", s); // OOPS!
        return 0;
    }
    

    经典的错误(4野指针)

    非法内存操作分析

    • 结构体成员指针没有初始化
    • 没有为结构体指针分配足够的内存

    例子

    #include<stdio.h>
    #include<malloc.h>
    struct Demo
    {
        int * p;
    };
    void main()
    {
        struct Dome d1;
        struct Dome d2;
        int i=0;
        for(i=0;i<10;i++)                     //问题1:不能直接赋值,因为并没有分配内存,也没有初始化。
        {
            d1.p[i]=i+1;
        }
        free(d2.p);
        d2.p=(int *)calloc(5,sizeof(int));
        for(i=0;i<10;i++)                    //问题2:只分配了5个空间,却用了10个。
        {
            d2.p[i]=i+1;
        }
        free(d2.p);
        return 0;
    }
    

    内存初始化分析

    • 内存分配成功但是没有初始化
    #include<stdio.h>
    #include<malloc.h>
    void main()
    {
        char* s=(char*)malloc(5);//不一定是 \0结束,所以不是字符串。没办法输出
        //s[4]='\0';
        printf(s);
        free(S);
    }
    

    内存越界

    • 数组越界
    void f(int a[10])
    {
        int i=0;
        for(i=0;i<10;i++)
        {
            a[i]=i;
            printf("%d\n",a[i]);
        }
    }
    int main()
    {
        int a[5];
        f(a);
        return 0;
    }
    

    内存泄漏分析

    #include <stdio.h>
    #include <malloc.h>
    
    void f(unsigned int size)
    {
        int* p = (int*)malloc(size*sizeof(int));
        int i = 0;
        if( size % 2 != 0 )
        {
            return; // OOPS!需要free掉,基数个size的情况
        }
        for(i=0; i<size; i++)
        {
            p[i] = i;
            printf("%d\n", p[i]);
        }
        free(p);
    }
    
    int main()
    {
        f(9);
        f(10);
        return 0;
    }
    

    C语言有关内存的规则

    用malloc申请了内存之后,应该立即检查指针值是否为NULL,防止使用值为NULL的指针

    int *p=(int *)malloc(5*sizeof(int));
    if(p!=NULL)
    {
    
    }
    free(p);
    

    牢记数组长度,防止数组越界操作,考虑使用柔性数组

    相关文章

      网友评论

      • 知识学者:有图炸了, 嵌入式的吗? 我学c好像没有这些东西。。
        PcDack:@东风冷雪 比较深的内容了。下午更图

      本文标题:C语言内存管理一本道来

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