变量、指针和关键字
两个口诀:
- 变量变量,能变,就是能读能写,必定在内存(RAM)里
- 指针指针,保存的是地点,32 位处理器中的地点都是 32 位的,无论是什么类型的指针变量,都是 4 字节
指针
- 对于 32 位处理器内里,地点是 32 位的,以是指针的大小为 4 字节,sizeof(p) = 4 ,sizeof(*p) = 指针所指向的类型所占的空间
变量
- 只读的常量一般放在 flash 中,以是只读的变量加上 const 可以节省内存,但偶尔候为了优化也可能会放在内存里
extern 关键字
- 如果想在 a.c 中引用 b.c 中的全局变量 int b ,需要在 a.c 中加入:
- extern int b
- 包含头文件 #include "b.h" ,然后在头文件中写 extern int b
留意 extern int b 不能被赋值!!!这个是声明,表示 b 是什么
不建议使用 extern,可以使用函数来进行值通报
static 关键字
- 对于全局变量,如果不加 static,全局变量的作用域为整个程序,加上 static 作用域就变为该文件了(函数前加 static 也是同样的作用)
- 对于在函数内界说的变量,加上 static 的变量仅会初始化一次,再次调用该函数时,仍为上一次函数调用的结果,不会再次初始化
volatile 关键字
- 不能自作主张优化变量,直接存取原始内存地点
- 应用场景:
- 停止服务程序中修改的供其他程序检测的变量,需要用 volitile
- 多任务环境中个任务间共享的标志加 volitile
- 存储器映射的硬件存储器要加 volitile
后面两个场景还没用到,等用到再具体补充
布局体
- 布局体是不占内存的,是一种类型,用布局体界说了变量之后(实例化)才会分配内存空间
布局体对齐
为什么会有内存对齐
- 平台原因:不是全部的硬件平台都能访问任意内存地点上的任意数据,某些硬件平台只能在某些地点处取某些特定类型的数据,否则抛出硬件异常。为了同一个程序可以在多平台运行,需要内存对齐。
- 硬件原因:经过内存对齐后,CPU 访问内存的速度大大提升。
对齐规则
- 布局体每个成员相对布局体首地点的偏移量是对齐参数的整数倍,如有需要会在成员之间填充字节。编译器在为布局体成员开辟空间时,首先检查预开辟空间的地点相对于布局体首地点的偏移量是否为对齐参数的整数倍,若是,则存放该成员,若不是,则填充多少字节,以达到整数倍的要求。
这里的对齐参数取每个变量自身对齐参数和系统默认参数#pragma pack(n)(一般为 8)中较小的谁人
- 布局体变量所占空间的大小是对齐参数大小的整数倍。如有需要会在最后一个成员末尾填充多少字节使得所占空间大小是对齐参数大小的整数倍。
这里的对齐参数取布局体中全部变量对齐参数的最大值和系统默认参数对比取较小的
图解
- | char | | | | 4字节
- | int | int | int | int | 4字节
- | short|short| | | 4字节
复制代码- | char | |short|short| 4字节
- | int | int | int | int | 4字节
复制代码 实例
- typedef struct
- {
- char c;
- short d;
- static int a;
- }A;
复制代码- | char | | 2字节
- | short|short| 2字节
复制代码 易错点:对于布局体中的 static int a ,静态数据成员存放位置与布局体实例的存储地点无关,不算在内里
只有 C++ 布局体中才有 static,C 语言中不允许有
- typedef struct
- {
- double b;
- int c;
- }D;
-
- typedef struct
- {
- bool a; // bool为1字节
- D d;
- double b;
- int c;
- }E;
复制代码- |bool|-----------------------------------| 8字节
- |--------------------D-------------------| 8字节
- |--------------------D-------------------| 8字节
- |------------------double----------------| 8字节
- |---------int--------|-------------------| 8字节
复制代码 易错点:D 与默认的 8 比,8 小,取 8 为对齐参数
变量赋值
- p = &a;
- *p = 123; // 将a的值变为123
复制代码- pt = &A;
- pt->age = 20; // pt为指针,取成员用"->",结构体用"."
- *pt = A2; // 将A变成A2的值
复制代码 布局体指针
- typedef struct student{
- char *name;
- int age;
- struct student *classmate; // 结构体中只能用指针,长度为4个字节(链表)
- }student, *pstudent;
- student zhangsan = {"zhangshan", 10, NULL};
- student lili = {"lili", 20, NULL};
- zhangsan.classmate = &lili; // 构成链表
- name = zhangsan.classmate->name; // zhangsan为结构体,用"."取值,classmate为指针,用"->"取值
复制代码 函数指针
- void (*add){}; // 函数指针,变量,占4字节
- typedef struct student{
- char *name;
- int age;
- void (*good_work)(void); // 函数指针
- struct student *classmate;
- }student, *pstudent;
- // add为函数指针,也可以写成&add
- // 函数指针是变量,所以可以赋值为地址,函数不是变量,只能被调用
- student ss[2] = {{"zhangshan", 10, add, NULL}, {"lili", 20, add, NULL}}; // 结构体数组
- ss[1].good_work(); // 结构体调用函数指针,用"."
- pstudent get(void){
- int type = 1;
- return &ss[type]; // 返回结构体指针
- } // 应尽量避免使用全局变量,可以将变量封装在函数里
- pstudent a;
- a = get();
- a->good_work(); //这里a为结构体指针,用"->"
复制代码 全局变量与局部变量
- 全局变量:断电时无,运行时有,初值来自 Flash
- 有值时初始化:类似于 memcpy,把 Flash 数据段团体拷贝到内存
- 初始值为 0/没有初始化的:这些变量在内存里都放到了 ZI 段,类似于 memset,把 ZI 段全部清零
- 局部变量:在栈里
参考资料
https://www.bilibili.com/video/BV1VM4y137Pm/?spm_id_from=333.999.0.0
https://blog.csdn.net/qq_41068271/article/details/83446623?spm=1001.2014.3001.5502
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |