memory manage - yaokun123/php-wiki GitHub Wiki

内存管理

一、内存管理

1、malloc:申请动态内存空间

2、free:释放动态内存空间

3、calloc:申请并初始化一系列内存空间

4、realloc:重新分配内存空间

注意这些函数都是在<stdlib.h>头文件中

1.malloc:申请动态内存空间(存放在堆中)

函数原型:void *malloc(size_t size);

malloc函数向系统申请分配size个字节的空间,并返回一个指向这块空间的指针。

如果函数调用成功,返回一个指向申请的内存空间的指针,由于返回类型是void指针(void *),所有它可以被转换成任何类型的数据;如果函数调用失败,返回值是NULL。另外,如果size参数设置为0,返回值也可能是NULL,但这并不意味着函数调用失败。

2.free:释放动态内存空间

动态内存是存放在堆中的,如果你不主动去释放堆上的内存资源,那么他将永远的存在,直到程序关闭。

函数原型:void free(void *ptr);

free函数将释放ptr参数指向的内存空间。该内存空间必须是由malloc、calloc、realloc函数申请的。否则该函数将导致未定义行为。如果ptr参数是NULL,则不执行任何操作。注意:该函数并不会修改ptr参数的值,所以调用后任然指向原来的地方(变为非法空间)。

#include<stdio.h>
#include<stdlib.h>
int main(){
    int *ptr;
    ptr = (int *)malloc(sizeof(int));
    if(ptr == NULL){//一般内存不够用的时候会失败
        printf("分配内存失败!\n");
        exit(1);
    }
    printf("请输入一个整数:");
    scanf("%d",ptr);
    
    printf("你输入的整数是:%d\n",*ptr);//520
    free(ptr);//释放
    printf("你输入的整数是:%d\n",*ptr);//0
    return 0;
}

3.内存泄露

导致内存泄露主要有两种情况:

  • 一、隐式内存泄露(即用完内存块没有及时free函数释放)
  • 二、丢失内存块地址

为防止内存泄露,我们要在使用完这块内存空间之后,立刻调用free函数释放(高级语言有垃圾回收机制,而C语言则没有)

int main(void){
    while(1){
        malloc(1024);//循环申请内存空间,并且没有及时释放,会导致内存爆掉
    }
    return 0;
}

4.calloc:申请并初始化一系列内存空间

函数原型:void *calloc(size_t nmemb,size_t size);

calloc函数在内存中动态申请nmemb个长度为size的连续内存空间(即申请的总空间尺寸为nmemb*size),这些内存空间全部被初始化为0

calloc函数在申请完内存后,自动初始化该内存空间为零

5.realloc:重新分配内存空间

函数原型:*realloc(void *ptr,size_t size);

6.初始化内存空间

/**
 以mem开头的函数被编入字符串标准库,函数的声明包含在string.h这个头文件中。
 --memset--使用一个常量字节填充内存空间
 --memcpy--拷贝内存空间
 --memmove--
 --memcpm--比较内存空间
 --memchr--在内存空间汇总搜索一个字符
 */

二、内存布局

#include<stdio.h>
#include<stdlib.h>

int global_uninit_var;
int global_init_var1 = 520;
int global_init_var2 = 880;

void func(void);

void func(void){
    ;
}

int mian(void){
    int local_var1;
    int local_var2;
    
    static int static_uninit_var;
    static int static_init_var = 456;
    
    char *str1 = "I love FishC.com!";
    char *str2 = "You are right!";
    
    int *malloc_var = (int *)malloc(sizeof(int));
    
    printf("addr of func -> %p\n",func);
    printf("addr of str1 -> %p\n",str1);
    printf("addr of str2 -> %p\n",str2);
    
    printf("addr of global_uninit_var -> %p\n",global_uninit_var);
    printf("addr of global_init_var1 -> %p\n",global_init_var1);
    printf("addr of global_init_var2 -> %p\n",global_init_var2);
    
    printf("addr of static_init_var -> %p\n",static_init_var);
    printf("addr of static_uninit_var -> %p\n",static_uninit_var);
    
    printf("addr of local_var1 -> %p\n",&local_var1);
    
    printf("addr of local_var2 -> %p\n",&local_var2);
}

/**
 代码段(Text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读。在代码段中,也有可能包含一些只读的常量。
 
 数据段(Initialized data segment)通常用来存放已经初始化的全局变量和局部静态变量。
 
 BSS段(Bss segment/Uninitialized data segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称,这个区段中的数据在程序运行前将被自动初始化为数字0。
 
 堆:是用来存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩展或缩小。当进程调用malloc等函数分配内存是,新分配的内存就被动态添加到堆上;当利用free等函数释放内存时,被释放的内存从堆中被剔除。
 
 栈:大家平时可能经常听到堆栈这个词,一般指得就是这个栈。栈是函数执行的内存区域,通常和堆共享一片区域。
 */




/**
 堆和栈的区别:
 
 一、申请方式:
    --堆是由程序员手动申请
    --栈是由系统自动分配
 二、释放方式:
    --堆是由程序员手动释放
    --栈由系统自动释放
 三、生存周期:
    --堆的生存周期由动态申请到程序员主动释放为止,不同函数之间均可自由访问
    --栈的生存周期由函数调用开始到函数返回时结束,函数之间的局部变量不能相互访问
 四、发展方向:
    --堆和其他区段一样,都是从低地址向高地址发展
    --栈则相反,是由高地址向低地址发展
 */
⚠️ **GitHub.com Fallback** ⚠️