在嵌入式开发中,我认为应用要比底层逻辑更重要也更易理解,从问题出发才会真正get到这些知识点的妙用,所以下边的知识点我把使用场景放到了第一块
最后更新于2023/04/06
持续更新 ing…
volatile
使用场景
- 程序使用rtos,多线程中都会读写的全局变量需要使用volatile定义
- 中断和主函数中都要读写的全局变量,需要使用volatile定义;
- 单片机的寄存器定义,当然这些变量已经由芯片厂商在库函数中完成了定义
概念
- 首先要了解“编译”这个概念。
- 就是将C语言等高级语言编译成汇编指令。这个编译只是编译了你的程序而已,编译器并不知道中断和主程序之间的关系,线程和线程之间的关系。
- volatile关键字是告诉编译器,这个变量是易变的,比如在写一个USB驱动程序的时候,有一个变量a是来表示usb设备的插拔状态,这个变量a是在中断中被改变的,但是在编译器看来,这个中断在纯软件编译中是不会执行的,只有硬件上触发外部中断的时候这个变量才会改变,硬件debug调试中可以看出这个变量改变与否。
- 进一步了解CPU,内存RAM
- 变量初始化保存在RAM中,在程序中调用变量的时候会读到CPU中进行运算处理,如果编译器认为这个变量在运算过程中不需要将修改的值每次都存到RAM中,就为了提升程序的运算速度只在CPU中运算这个变量(巨快,也就是被优化了)。
- 某个寄存器中存放了引脚的电平状态,因此cpu如果要知道这个状态的话,就必须去读取这个寄存器的状态变量,因此这个变量不准被优化,也就是要加上volatile关键字
开发注意事项
- 编译器和优化等级不要随意更换:
- 不同的编译器和不同的优化等级对程序的优化是不同的,因为可执行文件是机器码文件链接而成的,机器码文件又是汇编指令汇编而成的,因此需要保证汇编指令的内容
const
使用场景
- 把这个常量存入flash来节省内存RAM,还可以提高这个常量相关的执行指令的运行速度(因为只在cpu中运行了吧,这个常量存到flash中且不能修改,程序执行时候借用备份于cpu中的这个常量数值)
概念
- 把某个变量定义为只读的常量,告诉编译器把它存入FLASH中,且这个常量不能被任何人修改
Static
强龙不压地头蛇,如果存在全局同名的变量或函数,且本文件中如果也定义了一个static变量或者函数,也优先使用本文件中的
使用场景
- 当多人协作同一个工程的时候,发现不同文件中有相同的变量,编译失败,这个时候如果你不想改变量名字,可以给自己的变量加上static,编译成汇编后,你这个变量后边会带上一个随机编号以区分另一个名字一样的变量,“所以本质上对机器来说这两个变量是不同的”,对程序员来说可以方便阅读和编写代码。
概念
- 修饰局部变量时,该变量称为静态局部变量
- 修饰全局变量后,该变量只能在本文件中使用而不允许在其他文件中使用,这个全局变量被称为静态全局变量
- 存储位置
- 都存储在静态区(全局区),全局变量和静态变量是放在一块的,已初始化的全局变量和静态变量都放在.data段,未初始化以及初始化为0的全局变量和静态变量都放在.bss段(都在内存中)
- C语言中全局变量存放在哪个位置?_c语言全局变量放在哪里
extern
使用场景
b.c
文件中有一个全局变量b, 这时候a.c
文件也要用这个变量b,就可以用extern int b
,这句可以写在a.c也可以写在a.h中,也可以两边都写。
概念
- 表示这个变量在外部的其他文件中被定义过了,只是一个声明(不能是一个可执行的指令)
extern int a =2
是错误写法
Struct
使用场景
- 当一类变量需要被经常使用到,这类变量还需要不同的名字来相互区分,这边可以参考STM32 gpio配置的时候,就会用到结构体(当然厂家已经帮你定义好了结构体),无需重复定义变量节省时间。
- 巧妙使用结构体,可以优化代码,减少代码量和内存占用
- 创建一个某类型的结构体,如何只初始化结构体中的某些变量而不管别的变量(用到
.XXX = xxx; //.type = 0
)
知识点
- 结构体内不同变量的内存占用
struct person1{
char sex;
int age;
};
printf("%d\r\n",sizeof(struct person1));
printf("%d\r\n",sizeof(struct person1*));
//以上代码在32位编译下会打印什么内容,以及为什么?
//答:显示8,4。
//为什么?32位系统的内存对基数机械的变量不太友好,如果变量是一个字节,会直接占用4个字节空间,另外三个字节为空浪费掉;如果再加一个char mingzu, 内存占用还是8.因为把他分配到了sex变量旁边,处于同一组中
typedef
使用场景
typedef int A;
typedef int * B;
typedef struct Icd operation C;
typedef struct lcd operation * D;
typedef int (*add type) (int, int);
typedef struct person{
char sex;
int age;
}person;//person 代替了前面一大串struct person{...}
int (*add_type)(int,int);
add type f1;//与上面那个等效,请问那个更加方便?
概念
- 定义一个类型,把某个类型(包括某种类型的函数(同返回值,同参数))转换成某个简称,之后就可以等效替换使用了,非常方便