• JM8.6之内存分配——基础篇


      在研究JM8.6中内存分配模块(memalloc.c)时,看到如下代码(注意入参的byte ***):

      如果C语言基础较好的话,对上面的实现也比较好理解。也即是双指针的初始化。

      但是再看到下面:

      以及再下面:

      可真得花点时间去琢磨内存如何布局的,以及如何访问到期望位置的值。

      本篇文章就准备将背景知识和上面的三个内存分配函数,介绍一下,以供有此困惑的人来理解。

      其实,很早之前就有该想法,针对C语言的指针来做一次科普,但由于拖延症的缘故一直没动笔。

      本篇是基础篇,后面会再开一篇来介绍上面三个函数的原理及内存布局。

    1. 什么是指针?

      所谓指针,就是某个地址空间,存储着一个值(指针值),这个值为某个内存地址。

      如下图:(请原谅我拙劣的画图水平,拿win10自带的绘图工具画的。。。)

      

      拿32位系统来进行说明,每个指针(不管什么类型的指针——内置类型或自定义类型)的sizeof都为4Bytes,并且为

    了高效访问,一般都是4字节对齐的(存储ptr值的这段内存地址addr2,打印其值,最后两个二进制位为0)。

      上图中,内存空间addr2中存储着一个指针值——ptr(ptr的值为addr1),即这个ptr指针指向addr1这个地址空间。

    2. 栈指针 & 堆指针

      计算机系统中两种内存类型——stack和heap,stack为栈内存,如函数内临时变量、函数参数,heap为堆内存,如malloc

    分配的空间,其从系统中获取,一般分配和回收使用伙伴算法(buddy)。

      其中stack增长方向向下,heap增长方向向上,如下demo及打印:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 
     4 void stack_test()
     5 {
     6     int tmp0;
     7     int tmp1;
     8     int tmp2;
     9     printf("stack_addr: &tmp0=%p, &tmp1=%p, &tmp2=%p
    ", &tmp0, &tmp1, &tmp2);
    10 }
    11 
    12 void heap_test()
    13 {
    14     char *ptr0 = (char*)malloc(64);
    15     char *ptr1 = (char*)malloc(64);
    16     char *ptr2 = (char*)malloc(64);
    17     printf("heap_addr: ptr0=%p, ptr1=%p, ptr2=%p
    ", ptr0, ptr1, ptr2);
    18 }
    19 
    20 int main(void)
    21 {
    22     stack_test();
    23     heap_test();
    24 }

      从运行结果看,tmp0~2这种stack变量的地址,从高地址往低地址变化,而ptr0~2这种指向heap空间的值,从低往高变化。

    然而,ptr0~2作为stack类型变量,其地址仍符合stack的增长方向(从高往低变化)。不信可以打印出&ptr0, &ptr1, &ptr2的值。

    3. 如何给一个指针变量赋值?

      使用如下方式:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 
     4 void mem_alloc(char **pp)
     5 {
     6     *pp = (char*)malloc(64);
     7 }
     8 
     9 int main()
    10 {
    11     char *ptr0;
    12     char *ptr1;
    13     char *ptr2;
    14 
    15     char ch = 'A';
    16     ptr0 = &ch;                 //ptr0指向stack变量ch
    17     ptr1 = (char*)&ptr2;        //ptr1指向ptr2这个stack变量的地址
    18     mem_alloc(&ptr2);           //给ptr2这个stack变量赋值,赋值为heap空间地址
    19     printf("stack_addr: &ptr0=%p, &ptr1=%p, &ptr2=%p, &ch=%p
    ", &ptr0, &ptr1, &ptr2, &ch);
    20     printf("ptr_val: ptr0=%p, ptr1=%p, ptr2=%p
    ", ptr0, ptr1, ptr2);
    21 }

      需要注意一点的是,如果通过函数调用给一个变量初始化,那么参数必须是该变量的地址,如18行的:mem_alloc(&ptr2);

      为什么?

      传地址才能修改该地址处的值(ptr2这个stack变量的内存地址空间中,所保存的值——指向哪儿),而传值只是进行了一份数据拷贝,

    等调用的函数退出后,原先被拷贝的变量什么都没改变。

      因此,如果想修改15行中ch这个stack变量的值,就传其地址:&ch;如果想修改13行的ptr2这个stack变量的值,也传其地址:&ptr2,

    正如18行中所调用的。

    4. 指针、双指针、三指针、四指针

      指针也可以称为单指针,类似于这种:void* ptr; 如上面代码中的ptr0,ptr1,ptr2。

      双指针为指向指针的指针,类似于:void** pptr; 如第4行中的形参:void mem_alloc(char **pp)。

      三指针其实又多了一个*,形如:void*** ppptr; 可以理解为一个指针指向一个双指针,而该三指针变量值(该变量内存空间中的值)为第一个指针值。

      四指针也又多了一个*,形如:void**** pppptr; 再多一道中转。

      画个图来表达:

      

       其中,存储字符的空间我故意画小,因为其大小为1Byte,而指针的大小为4Bytes。

  • 相关阅读:
    STM32 printf 方法重定向到串口UART
    STM32F401CCU6与MFRC522接线及读取示例
    Keil MDK5 STM32F401CCU6开发环境配置
    Keil MDK5 STM32F103C8T6开发环境配置
    RFID EPC Class1 Gen2电子标签笔记
    Ubuntu20.04下的ESP8266环境
    Centos7使用memtester测试内存
    内核5.4以上, Realtek 8111网卡初始化失败
    Centos7的KVM安装配置详解
    Python抓取网页例子
  • 原文地址:https://www.cnblogs.com/Dreaming-in-Gottingen/p/14287296.html
Copyright © 2020-2023  润新知