博客

  • C语言基础

    1. 指针

    指针是嵌入式开发用得最多的,直接操作硬件地址全靠指针。

    重点理解:

    • 指针就是地址,指针变量存储的是一个内存地址
    • 指针和数组的关系,arr[0]*arr 是什么关系
    • 二级指针,指针的指针
    • 函数指针 —— 这个在驱动里用得非常多

    举个例子:操作硬件寄存器,就是这么干的:

    // GPIOA端口的基地址是 0x40020000
    #define GPIOA_BASE  0x40020000
    // 数据寄存器偏移是 0x14
    #define GPIOA_DATA  (GPIOA_BASE + 0x14)
    // 通过指针读写这个寄存器
    *((volatile unsigned int *)GPIOA_DATA) = 0xFF;

    看到了吗?就是把地址强制转换成指针,然后解引用读写。不理解指针,这段代码你就看不懂。

    2. 结构体

    结构体用来干嘛?表示硬件寄存器啊!

    一个外设往往有很多个寄存器,它们的地址是连续的,我们可以定义一个结构体来对应:

    typedef struct {
        unsigned int MODER;    // 模式寄存器,偏移 0x00
        unsigned int OTYPER;   // 输出类型寄存器,偏移 0x04
        unsigned int OSPEEDR;  // 速度寄存器,偏移 0x08
        unsigned int PUPDR;    // 上拉下拉寄存器,偏移 0x0C
        // ... 其他寄存器
    } GPIO_TypeDef;
    
    #define GPIOA ((GPIO_TypeDef *)GPIOA_BASE)
    // 然后就可以这么用了
    GPIOA->MODER |= (1 << 0);  // 设置PA0为输出模式

    结构体这里要注意内存对齐,不对齐的话寄存器地址就错了,程序肯定跑飞。

    3. 内存管理

    • mallocfree ,什么时候用,为什么要避免内存泄漏
    • 栈空间和堆空间的区别 —— 嵌入式栈空间一般很小,别定义太大的局部变量
    • 野指针问题 —— 用完指针记得置空,释放了别再访问

    4. 函数指针

    驱动框架里大量使用函数指针,把你的操作函数注册到框架里:

    struct file_operations {
        struct module *owner;
        loff_t (*llseek) (struct file *, loff_t, int);
        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
        // ... 很多函数指针
    };

    这是Linux内核里的字符设备操作函数集合,你要实现read、write这些函数,然后把地址赋值给函数指针,内核就会调用你的函数。不理解函数指针根本看不懂驱动代码。

    C语言学到什么程度够了

    不用你去写复杂的算法,也不需要精通C++,把上面这些基础概念吃透,能看懂别人的代码,能写出简单的模块就够了。边做项目边提高完全没问题。

    C语言推荐学习资料

    • 书籍:《C Primer Plus》适合入门,《C和指针》进阶,《深入理解计算机系统》讲指针和内存
    • 练习:去LeetCode刷一些简单题,重点练练数组、链表操作