指针与内存管理

指针是 C 的灵魂,也是嵌入式工程师必须掌握的核心。

一、指针基础

int   x = 42;
int  *p = &x;       // p 指向 x
*p = 100;           // 改 x 的值
int **pp = &p;      // 指向指针的指针

int  arr[5] = {1,2,3,4,5};
int  *q   = arr;    // 数组名隐式转指针
*(q + 2)  == arr[2] // 3
q[2]      == arr[2] // 数组下标是 *(q+2) 的语法糖

void *vp = malloc(16);   // void* 可转任何指针(C 不必强转;C++ 要)
free(vp);

二、指针算术

char  *cp = (char*)arr;     // 每次 +1 移动 1 字节
int   *ip = arr;            // 每次 +1 移动 sizeof(int)=4 字节

三、内存布局(典型进程)

低地址
┌────────────────┐ 0x0000_0000
│     NULL       │
├────────────────┤
│ .text  代码段   │ 只读、可执行
├────────────────┤
│ .rodata 常量   │ 只读
├────────────────┤
│ .data 已初始化 │ 可读写
├────────────────┤
│ .bss  未初始化 │ 可读写,运行时清零
├────────────────┤
│   heap 堆      │ malloc / new 向上增长
│       ...      │
│       ↓        │
│       ↑        │
│   stack 栈     │ 局部变量、函数调用
├────────────────┤ 0xFFFF_FFFF
高地址

注:实际地址受 ASLR、MMU 影响,但相对位置不变。

四、栈帧(Stack Frame)

void foo(int a) {       // a 在栈
    int local;          // local 在栈
    char buf[64];       // buf 在栈(注意溢出!)
    bar(local);         // 压栈:返回地址、参数
}

栈帧布局(高位在上):

┌────────────────┐ ← 旧栈帧
│  旧栈帧内容     │
├────────────────┤
│  返回地址 (LR)   │
├────────────────┤
│  上一帧 FP      │
├────────────────┤ ← FP (frame pointer)
│  局部变量       │
└────────────────┘ ← SP

五、堆与栈对比

特性
分配速度 快(指针下移) 慢(可能寻空闲块)
空间 较小(MB 级) 较大(GB 级)
生命周期 自动 手动
碎片 可能有
多线程 每线程独立 共享
嵌入式 实时性更好 慎用(碎片、OOM)

六、内存对齐(Alignment)

struct s {
    char  a;     // 1 字节
    int   b;     // 4 字节,对齐到 4 边界
};               // 总大小 8(不是 5!)

为什么要对齐?
- CPU 访问未对齐数据可能:性能下降、抛异常(ARMv7 默认支持,但慢)
- 硬件寄存器、DMA buffer 必须对齐到自然边界

对齐控制:

_Alignas(16) int buf[4];                    // C11
int buf[4] __attribute__((aligned(16)));    // GCC
#pragma pack(1)                              // 紧凑(1字节对齐)

七、常见内存错误

错误 现象 工具
缓冲区溢出 数据破坏、栈溢出、漏洞 ASan / Stack Smasher
Use After Free 数据破坏、崩溃 ASan
Double Free 堆破坏 ASan
内存泄漏 长时间运行 OOM Valgrind / ASan / LSan
未初始化内存 随机值 MSan
// Valgrind 推荐用法
valgrind --tool=memcheck --leak-check=full ./program

// GCC AddressSanitizer
gcc -fsanitize=address -g program.c -o program

八、嵌入式专用技巧

struct flags {
    uint32_t enable : 1;
    uint32_t mode   : 3;
    uint32_t        : 4;   // 填充
    uint32_t value  : 24;
};