【C语言】动态内存申请,堆空间与栈空间差异

发布时间 2024-01-13 00:21:09作者: hzyuan

我们在学习完C语言的数组后都会觉得数组长度固定很不方便,其实C语言的数组长度固定是因为其定义在栈空间,而栈空间的大小在编译时是确定的。如果使用的空间大小不确定可以使用堆空间。

#include <stdio.h>
#include <string.h>
#include <stdlib.h> //malloc\free使用的头文件

int main() {
    int size; //所申请空间的大小
    char *p;
    scanf("%d",&size);
    p=(char*)malloc(size); //申请空间
    strcpy(p,"hello");
    puts(p);
    free(p); //释放空间
    return 0;
}

malloc函数和free函数

//动态申请空间
void* malloc(unsigned int _Size);

//释放所申请的空间
void free(void *_Memory);

malloc 函数会动态申请 _Size 字节大小的堆空间,返回值为 void* 类型的指针(void* 类型的指针只能用来存储一个地址而不能进行偏移),malloc并不知道我们申请的空间用来存放什么类型的数据,所以要通过强制类型转换将 void* 类型转为所需类型。

free 函数的传入参数为 void* 类型,任何指针均可自动转换为 void* 类型。
申请堆空间时,内核中记录的是起始地址和大小,所以 free 函数要传入申请时的首地址进行匹配,而不能进行传入偏移后的地址。

堆空间与栈空间的差异

为什么要分栈空间和堆空间?
栈是计算机系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈\出栈操作都有专门的指令执行;
堆是C/C++函数库提供的数据结构,机制十分复杂,例如为了分配一块内存,库函数会按照一定的算法在堆内存中搜索大小满足的空闲空间,如果没有找到,就有可能调用系统功能去增加程序数据段的内存空间。
显然,堆的效率比栈低得多。

栈空间由系统自动管理,堆空间的申请和释放需要自行管理。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//堆空间和栈空间的差异

char* print_stack() {
    char c[20]="I am print_stack";
    puts(c);
    return c;
}

char* print_malloc() {
    char *p;
    p=(char*) malloc(20);
    strcpy(p,"I am print_malloc");
    puts(p);
    return p;
}

int main() {
    char *p;
    p = print_stack(); //数据放在栈空间
    printf("p=%s\n",p);
    p = print_malloc(); //数据放在堆空间
    puts(p);
    free(p);
    return 0;
}

执行结果:

为什么会打印 p=(null)?原因是 print_stack() 函数中的字符串存放在栈空间中,函数执行结束后,栈空间被释放,函数内的所有局部变量消失。而 print_malloc() 函数中的字符串存放在堆空间中,堆空间只有在执行 free 操作后才会释放,否则在进程执行过程中会一直存在。