1. 指针的本质:从嵌入式视角看内存操作
在嵌入式开发中,指针可能是最让人又爱又恨的特性。我至今记得第一次用STM32操作GPIO时,看到GPIOA->ODR = 0xFFFF;这种写法时的困惑——这箭头到底是什么意思?后来才明白,这其实就是指针在嵌入式硬件寄存器访问中的典型应用。
1.1 内存地址的物理意义
在Cortex-M内核的MCU中,每个外设寄存器都有固定的内存映射地址。比如STM32F103的GPIOA寄存器组通常位于0x40010800起始的位置。当我们声明GPIO_TypeDef *GPIOA时,实际上是在告诉编译器:"这个指针变量里存放的是0x40010800这个地址,从这个地址开始按GPIO_TypeDef结构体的方式解释数据"。
关键理解:指针变量本身也占用内存空间(通常是4字节),它存储的值是另一个内存单元的地址。在32位嵌入式系统中,这个地址值就是实实在在的物理内存地址。
1.2 声明中的*:类型修饰符
当我们在变量声明中看到*时,它是在定义一种新的数据类型。例如:
c复制uint32_t *p_reg; // 定义一个指向uint32_t类型的指针
char *p_str; // 指向char类型的指针
这里的*不是运算符,而是类型声明的一部分。它告诉编译器:
- 分配4字节空间(32位系统)用于存储地址
- 当对这个指针解引用时,应该按照指定类型解释数据
在嵌入式开发中,常见的指针声明场景包括:
- 外设寄存器映射(如
TIM1->CR1) - 动态内存管理(malloc返回的指针)
- 字符串处理(字符指针)
- 回调函数(函数指针)
1.3 运算中的*:解引用操作
当*作为一元运算符出现在表达式里时,它执行的是解引用操作。例如在STM32 HAL库中常见的写法:
c复制*((volatile uint32_t *)0x40021018) = 0x00000001;
这行代码的意思是:
- 将数值0x40021018强制转换为指向uint32_t的指针
- 对这个指针解引用,即访问该地址处的32位寄存器
- 写入值0x00000001
在嵌入式环境中,解引用操作直接对应着对物理内存的读写。这也是为什么我们经
