指针与数组的关系:

这里的指针存的是这个数组的第一个空间的地址,即首地址,所以** p等价于b[0] **



这里可以看到同样的(p+2),不同类型的指针和数组也是不一样的,char类型是加一个字节,int类型则是4个字节
下面是一些指针与数组的关系:

多维数组:

这里可以理解为四行二列的数组,这也是为什么在做简易智能手表项目时,图标要定义为一个二维数组,它将第一维作为索引来选择图标,二维用来存放图标数据,这样就能实现一个数组放多个图标的功能。
指针数组:


我的理解就是在指针数组里面存放的都是这个类型的指针,比如图中char* c[4]就有四个连续char类型的指针变量
例如图二,在指针数组中,arr[0]存放的就是变量a的首地址


这两个图解释了,为什么一个char*类型的指针明明只有四个字节大小,为什么能放一个“Hello”大小的字符串,这是因为指针数组存放的是这个字符串的首地址,而不是这个字符串,这里与上面数组和指针的关系中,为什么能将数组这么大的存储空间赋给指针一样,因为指针存储的是这个数组的首地址,所以只要pritnf这个指针数组的值(即这个字符串的首地址),它就会沿着这个首地址将这个字符串打印出来,直达遇到‘/0’。
结构体:


简单来说结构体就是用struct来重新定义的一个数据类型,你可以用**.** 来访问这个结构体的变量
结构体的内存分布:


可以看到结构体里的第一个变量是在最低地址,然后以此类推。
而且为了提高cpu的运行效率,如果定义一个char类型内存只有一个字节的大小的变量,它会将后面的三个字节大小的空间自动填充,闲置来实现内存对齐,然后将另外一个类型的变量放在下一行地址上。
结构体指针:


这两张图解释了什么是结构体指针,简单来说就是将结构体的首地址赋值给指针,这跟上面的将指针数组很像,也是将字符串的首地址赋值给指针

将结构体的首地址赋给指针后,指针就可以通过**->**来访问结构体的成员
使用结构体指针访问硬件:



如果要访问GPIO各个成员的地址,就要定义很多个指针来存储这些地址,那么结构体指针就很轻松解决了这个问题,这里直接将GPIO结构体的首地址赋值给p,然后p就可以通过**->来访问GPIO的成员,然后通过位运算**来实现对每个寄存器成员的操作。
那为什么一定要用指针呢?定义一个结构体变量不行吗?结构体变量不也是能实现对成员的访问吗?,图三给出了答案,因为结构体变量是在内存中分配一块新的区域给它,它能赋予这块区域某个地址一个变量,但这跟GPIO寄存器没有关系,因为结构体变量的一块全新的区域,它跟GPIO寄存器这块区域没有任何关系,你访问的只是这个结构体的成员,而不是GPIO寄存器的成员,所以就要把GPIO首地址强转换为结构体指针这样就可以实现操作这块固定位置的寄存器
所以可以简单解释为:
**普通结构体变量:**定义时编译器会在 SRAM 中全新分配一块空闲内存。这块内存是独立的 “数据副本”,和 GPIO 外设寄存器的固定物理地址没有任何关联。你修改它的成员,只是修改了 SRAM 里的普通数据,完全碰不到硬件寄存器,自然控制不了引脚。
**结构体指针:**我们手动把 GPIO 外设的固定物理基地址,强制转换成对应结构体类型的指针。相当于让结构体的布局直接 “套” 在硬件寄存器的内存区域上,此时通过 -> 访问成员,就是直接读写真实的硬件寄存器




