1

1.基本概念

  • 堆(Heap)
    程序可以动态申请的存储空间,通过malloc系列函数分配,全局可访问。

  • 栈(Stack)
    这里说的栈不是数据结构中LIFO的栈,而是进程虚拟地址空间的栈;程序在进行函数调用时动态伸缩的存储空间,局限于函数内可以访问。

-堆变量(Heapvariables)
数据存储在堆的变量,全局可访问。

  • 栈变量(Stackvariables)
    函数中声明的局部变量,只能在函数内部访问,否则访问行为的结果是未定义的。

  • 指针参数(Pointerparameters)
    参数类型为指针的参数。

  • 非指针参数(Non-pointerparameters)
    参数类型不是指针的参数。

2.进程的虚拟地址空间

进程虚拟地址空间

3.相关问题剖析

3.1 如何正确的分配内存

demo代码

void * fun_m1()
{
    char buf[100];
    return (void *)buf;
}

void * fun_m2(size_t size)
{
    return malloc(size);
}

void fun_m3(size_t size, void * p)
{
    p = malloc(size);
}

void fun_m4(size_t size, void ** p)
{
    *p = malloc(size);
}

剖析

  • fun_m1是错误的,因为它返回的栈变量的地址,如果对它指向的地址进行读写,程序行为的结果是未定义的,程序很可能崩溃,因为此时栈变量的空间已经被回收(栈顶指针改变了)。

  • fun_m2是正确的,因为它返回的是malloc申请的堆空间的地址。

  • fun_m3和fum_m4很具有迷惑性,要分区fun_m3和fun_m4的区别,我们这里需要澄清一个概念:任何的参数传递本质上都是值拷贝,任何参数都是栈变量。

  • 在我们以往观念中参数传递就是两种:值传递,指针传递,而通过指针可以改变指针指向的变量。

  • 为什么说参数传递都是值拷贝呢,这是因为不管参数是否为指针,传递的都是一份值的拷贝,只不过当你的参数类型为指针时,你传递的是指针变量的值,而通过*操作符作用在指针变量上,你又刚好可以影响到指针变量关联的其他变量的值。

  • 通过上面的剖析我们可以知道fun_m3是错误的,因为没有*操作符作用在p上,单纯对p赋值操作只会影响到局部的栈变量p的值,而不会对函数fun_m3外的变量有任何影响。fun_m4是正确的,因为有*操作符作用在p上,通过对*p赋值来修改传递给p的参数,使它指向申请的堆空间。

3.2 如何判断堆和栈的“增长”方向(从低到高,还是从高到低)

判断栈的“增长”方向

#include <stdio.h>
#include <malloc.h>

void fun1(int * pb)
{
    int a;
    printf("stack alloc direction[%s]\n", &a > pb ? "Up" : "Down");
}

void fun2()
{
    int b;
    fun1(&b);
}

int main()
{
    fun2();
    return 0;
}

编译运行

[root@iZ940zytujjZ test]# gcc -o test10 test10.c
[root@iZ940zytujjZ test]# ./test10
stack alloc direction[Down]
[root@iZ940zytujjZ test]#

从运行结果看,栈的增长方向是“从高到低”(Down)。

判断堆的“增长”方向

#include <stdio.h>
#include <unistd.h>
#include <malloc.h>

int main()
{
    void * a = sbrk(10);    //调整堆顶指针brk
    void * b = sbrk(20);
    printf("heap alloc direction[%s]\n", b > a ? "Up" : "Down");
    return 0;
}

编译运行

[root@iZ940zytujjZ test]# gcc -o test11 test11.c
[root@iZ940zytujjZ test]# ./test11
heap alloc direction[Up]
[root@iZ940zytujjZ test]#

从运行结果看,堆的增长方向是“从低到高”(Up)。


后端开发工程实践
1k 声望98 粉丝

资深的后端研发工程师,在后端研发领域深耕10多年。曾在腾讯、字节跳动等知名公司从互联网商业化服务中台等领域的工作。