嵌入式八股C语言---关键字篇
C语言的关键字一共有32(C89) + 5(C99),至于后面的先不介绍啦。
关键字
C语言的关键字一共有32(C89) + 5(C99),至于后面的先不介绍啦
1. 数据类型关键字

- 数据类型的大小
- char : 1个字节 [-128,127] [0,255]
- int : 一般是4个字节(对于16位操作系统则是2个字节)
- short : 不少于2个字节
- long : 至少四个字节
- float : 一般4个字节
- double : 一般8个字节
- 指针的大小: 指针就是地址 32位操作系统就是 4个字节 64位操作系统就是 8个字节
- struct的大小
想要知道 struct的大小 得先了解一下数据对齐
- 2.1 数据对齐
一般为了加快读取速度,连续定义的变量地址也不一定是连续的,比如:
int main()
{
int a;
char b;
double c;
printf("%p %p %p\r\n",&a,&b,&c);
printf("%d %d\r\n",sizeof(int),sizeof(int *));
return 0;
}
变量的起始地址都是能整数自己的所占用的字节数
0x7fff49cc26ac(整除4) 0x7fff49cc26ab(整除1) 0x7fff49cc26a0(整数8)
-
2.2 struct的大小
(1) 起始地址:能够整除自己的最大数据类型成员所占字节数
(2) 每个成员地址偏移量的对齐
(3) 结构体总大小的对齐:按照最大数据类型成员所占字节的整数倍
-
2.3 强制对齐
#pragma pack(n)
#pragma pack(2)
struct stu
{
char a;
int b;
char c;
};
#pragma pack()
// 结果就是8
-
2.4 根据struct的成员变量推导起始地址—offset_of宏
#define offset_of(type, member) ((size_t)&(((type )0)->member))
#define container_of(type,member,ptr) ((type)((size_t)ptr - offset_of(type,member)))
核心思想就是定义一个 起始地址为0的指针 通过找到对应的成员变量的地址 从而就能确定偏移量了
-
2.5 位域
// 定义一个模拟IP头部分字段的结构体
struct IPHeader {
unsigned int version : 4; // 版本号,占4位(实际int 站32位)
unsigned int headerLen : 4; // 头部长度,占4位(以32位字为单位)
unsigned int typeOfService : 8; // 服务类型,占8位
unsigned int totalLength : 16; // 总长度,占16位
};
// 好处是减少了结构体占用的空间
// 坏处是编译器可能不会支持找地址了
应用的话 比如对寄存器的操作
struct ControlRegister {
unsigned int ENABLE : 1; // Bit 0
unsigned int MODE : 1; // Bit 1
unsigned int PRIORITY : 2; // Bits 2-3
unsigned int RESERVED : 4; // Bits 4-7
}; // 实际上就是占用了8位 1个字节 但是这样操作寄存器的可读性会好很多(当然一不小心超了是啥样就没人知道了)
- union
联合体:大小决定于占用空间更大的成员 同时要注意按最大数据类型成员的对齐方式对齐
union stu{
char a[10];
int c;
};
占用了10个字节,但是对齐方式按int的方式对齐 所以最终占用了12个字节
- 3.1 匿名联合体
struct test_uion
{
int height;
char num;
union {
char name[10];
int id;
};
};
//你可以用name操作 也可以用id操作 因为本质上这里就是**一块12字节的内存** 名字不同操作方式不同
- 3.2 union的妙用:测试大小端
0x1234 34是低位置字节 12是高位置字节
大端: 高地址存放低位置字节
小端: 高地址存放高位置字节
union test_edian {
char a[2];
short c;
}
union test_edian test;
test.c = 0x1234;
栈的地址增长方向是高地址向着地地址
所以 printf(“%x”,test.a[0]); // 0x12是大端 0x34是小端
- enum枚举
枚举的大小并不固定 编译器会进行优化
2. 存储类关键字
注意所有的存储类关键字不能一起使用!
auto extern register static 这四个不能组合
- static 关键字
- static的作用
- static 修饰函数/全局变量:这个函数/全局变量不能在本文件外被调用了
- static修饰的变量放在数据段或者bss段(未初始化)–此时变量生命期为整个程序(局部静态变量不随着函数调用退出而被销毁)
- 作用域:局部静态变量在{}内 / 全局静态变量被限制在这个文件当中
- static的作用
- auto关键字
auto变量存放在栈上 一般随着函数调用结束就返回了 - register关键字
建议把变量放在cpu寄存器中
此时就不要对这个变量取地址了 不能对寄存器取地址 - extern关键字
外部变量声明,是指这是一个已在别的地方定义过的对象
extern关键字在链接期间起作用
3.控制类关键字
这个就没啥好解释的 别用错了就行


3. 其他
const volatile typedef sizeof
- sizeof 关键字
以字节为单位计算数据类型大小
int a[10]; //sizeof(a)的大小就是40
- 1.1sizeof 和 strlen的区别
- strlen计算字符串的长度,以’\0’为结尾
- sizeof计算的是数据类型
比如 const char *str = “123456”;
sizeof(str) 结果就是8 因为str是个指针 在64位操作系统占8个字节
strlen(str) 结果是6 这是字符串的长度 - sizeof(“\0”); //大小是2
- sizeof(a++);//++直接没用了
- 不用sizeof求大小
#define mysizeof(type) ((char * )(&value + 1) - (char *)(&value))
- const
- 2.1 常量指针与指针常量
- 常量指针:指向的值不能变 但是指针自己可以变
const int *p = &a; *p = 5;//错误的 p = &b;//正确的 - 指针常量:指针是常量不能变,但指针指向的值可以变
int * const p = &a;
p = &c;//错误的 *p = 5;//正确的
// 所以引用就是指针常量
const int * const p; // 啥都不能变
- 常量指针:指向的值不能变 但是指针自己可以变
- 2.2 const与 #define
- define 是在预处理阶段起作用 而 const是在编译运行阶段起作用
- define 本身只是字符串替换 不会像const一样占用数据段空间
- const 定义变量本身是有类型的 会有类型检查
- const 在调试的时候方便 define不能调试 预处理阶段就取消了
- define 的定义可以被undefine掉 更加灵活
- volatile
防止编译器去优化变量,每次都去内存取而不是寄存器缓存的
-
3.1 什么时候用volatile
中断可能会修改这个变量的时候
并行设备的硬件寄存器 -
3.2 volatile int * 和 int * volatile
- volatile int *
修饰的是int 说明这个指针指向的变量是易变的 - int * volatile
说明这个指针是异变的
- volatile int *
-
3.3 const 和 volatile能连用吗
当然可以
虽然warning了 但是内个值还是被改变了
为啥呢?- const只在编译期间有用, 表示一个变量在程序编译期不能被修改且不能被优化
- volatile表示在程序运行期,变量值可修改,但每次用到该变量的值都要从内存中读取,以防止意外错误
比如对于某些只读寄存器的访问 它里面的值虽然是只读的 但是外部他也可能把值变了 那假如编译器优化了 就完了
- typedef
- 4.1 typedef和宏的区别
- typedef是关键字 编译会进行检查; define是预处理阶段的简单替换
- 比如 #define my_ptr int* my_ptr a,b; //此时b就是int类型
- 但是 typedef int* my_ptr; my_ptr a,b; // 这就是俩指针
- 4.2 typedef的用处–定义一个好看一点的函数指针

4. C99新增
- inline关键字
内联函数 就是建议编译器把这个函数在调用位置直接展开- 相较于普通函数
- 减少了函数调用入栈/出栈的开销
- 但是增加了代码体积
- 内联函数与宏
- 内联函数有参数的检查;宏没有只是替换
- 函数支持调试;而宏在预处理阶段替换后就无了 不支持调试
- 内联函数为什么定义在头文件中
因为这样做直接包含这个头文件就可以像宏一样被使用了,如果放在头文件但是不用static修饰,被多个头文件包含时就会报错
- 相较于普通函数
- _Bool关键字
布尔类型的 - restrict关键字
告诉编译器该指针是访问某一内存区域唯一途径。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)