首页 > 技术文章 > C语言的堆内存管理以及与栈内存的区别

mushrain 2021-05-07 16:59 原文

堆内存管理

2021-5-7

Mushroom

与栈内存的区别之处?

1,分配模式

先简单介绍下堆栈内存到底是存储的什么?

栈内存一般存在两种分配方式静态分配动态分配

  • 静态分配:指的是编译器编译的时候就已经分配好的内存。
    • 你可以这样理解:最开始编译之后,静态分配的内存信息存储在了二进制文件信息中,每调用一次,编译器就会自动的分配这些内存。(不是很严谨啦,(●'◡'●))
  • 动态分配:指的是随着程序的运行动态的分配的,
    • 就比如说当程序运行到了ptr = (int *)malloc(sizeof(int) * 10)(注意这里的这个函数申请的空间是在堆内存中的!!),

针对于堆栈的两种动态分配内存的方式:

  • 栈内存的动态分配使用alloca(),不过不是很推荐

  • #include <stdio.h>
    #include <stdlib.h>
    
    
    int main() {
        unsigned int *a;
        unsigned int *b;
        a = (unsigned int *) alloca(sizeof(unsigned int) * 8);
        b = (unsigned int *) alloca(sizeof(unsigned int) * 8);
        for (int i = 0; i < 8; i++) {
            a[i] = 4294967295;
            b[i] = 286331153;
        }
        for (int i = 0; i < 8; i++) {
            printf("%d\n", a[i]);
        }
        return 0;
    }
    
  • 内存从低地址到高地址,并且连续,不容易产生内存碎片,这一点非常符合栈内存的分配方式。

  • 虽说不产生内存碎片,但是这容易造成访问越界

  • #include <stdio.h>
    #include <stdlib.h>
    
    
    int main() {
        unsigned int *a;
        unsigned int * ptr;
        unsigned int * head;
        head = (unsigned int *) malloc(sizeof(unsigned int));
        ptr = (unsigned int *) malloc(sizeof(unsigned int));
        ptr = (unsigned int *) malloc(sizeof(unsigned int));
        ptr = (unsigned int *) malloc(sizeof(unsigned int));
        a = head;
        a[3] = 286331153;
        a[2] = 286331153;
        a[1] = 286331153;
        a[0] = 286331153;
        for (int i = 0; i < 4; i ++) {
            printf("%d\n", a[i]);
        }
        return 0;
    }
    

  • 这个程序的连续的空间申请,如果是alloca()必然会造成访问越界出现。(不是数组,结果可以当作数组来使用)

  • 如果换成堆内存分配呢?malloc()
  • 符合预期,自上而下的分配内存,而且不连续,容易产生内存碎片

2.生存期

堆内存的生存期是直到其被free()主动释放,或者程序终止,内存自动释放。

栈内存的生存期是随着程序的运行自动结束释放的,如main函数里面如果使用alloca()函数或者是int a = 0,这样的内存申请,其生存期其实都是整个main生存期,如果说是在函数里面定义申请的内存的话。

void function() {
	int a = 0;
    int *ptr;
    ptr = (int *)alloca(sizeof(int) * 10);
    return;
}

这样的内存其生存期就存在于函数调用时的生存期,如果函数调用结束之后,自然而然的释放了,无需手动释放。

3.可获取空间大小

一般来说栈空间可申请的内存相对较小,

Linux下8M(我的就是8M)

windows下大约1M

小总结

栈内存可以动态分配,与一些变量声明,函数调用无异样,但是要注意的是,栈的空间其实是有限的,如果手动的在栈空间分配过多的内存就会导致stackoverflow(栈溢出)

尽量使用栈存储函数的调用信息,或者是一些大小相对较小的变量的存储,而且由于自动内存释放,使用的时候需要考虑可能无法返回的问题。

堆内存的使用

1.申请

申请的时候大小一般没有限制,但是考虑到堆内存的内存碎片,可能无法分配满,所以也不要一下子分配的内存太大了。

void *__cdecl malloc(size_t _Size);

申请_Size个大小的空间,申请成功返回头地址。如果失败则返回NULL

空间内存的数据未初始化,只是得到了这么大的空间

2. 初始化

void *memset(void *s, int c, size_t n);

初始化值

推荐阅读