
再认识一遍嵌入式的C语言
再认识一遍嵌入式的C语言
简单看看
在 C 语言中,不同类型占用的字节数是由编译器和平台决定的,但有一些常见的默认规则,尤其是在使用 32位或64位系统 时(比如 x86/x64 架构,Linux/Windows 常见编译器如 GCC、MSVC)。
类型 | 字节数(常见) | 说明 |
---|---|---|
char | 1 | 固定为 1 字节(这是 C 标准规定的) |
int | 4 | 通常为 32 位 |
long | 4 或 8 | 在 32 位系统是 4,64 位 Linux 是 8,Windows 仍是 4 |
long long | 8 | 通常是 64 位 |
float (IEEE 754) | 4 | 单精度浮点数(32 位) |
double (IEEE 754) | 8 | 双精度浮点数(64 位) |
- uint8_t unsigned char
- int8_t
- int16_t
- uint16_t
- int32_t
- uint32_t
- int64_t
- uint64_t
- float与fp32
- double与fp64
需要深入了解的老朋友
一、枚举
在 C 语言中,枚举是一种用户自定义的数据类型,它由一组命名的整型常量组成。这些常量称为“枚举常量”或“枚举值”。
✅ 枚举的作用: 提高代码可读性:将一些整数赋予有意义的名字 增强类型安全性(虽然 C 的枚举并不强制) 表示有限的状态集合(如状态机、选项等)
1.枚举的基本用法
enum 枚举标签 {
枚举常量1,
枚举常量2,
...
};
// 例如:
enum Color {
RED, // 第一个默认是0
GREEN,
BLUE
};
// 当然也可以显式的赋值
enum Color {
RED = 10,
GREEN = 20,
BLUE = 30
};
枚举变量的声明方式
方式1:先定义枚举类型,再声明变量
enum Color {
RED,
GREEN,
BLUE
};
enum Color c1, c2;
方式2:定义枚举的同时声明变量
enum Color {
RED,
GREEN,
BLUE
} c1, c2;
方式3:匿名枚举 + 变量声明(不推荐)
enum {
UP,
DOWN,
LEFT,
RIGHT
} direction;
这种方式适合一次性使用,但无法再次声明同类型的变量。
在 C 语言中,枚举本质上就是整数类型(通常是 int),每个枚举常量对应一个整数值。
例如:
enum State {
OFF,
ON,
ERROR
};
编译器会将其理解为:
#define OFF 0
#define ON 1
#define ERROR 2
枚举与 typedef 结合使用(推荐做法)
为了简化写法,通常我们会结合 typedef
使用枚举:
typedef enum {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
} Day;
Day today = FRIDAY;
现在你可以直接使用:
Day today;
而不需要每次都写成:
enum Day today;
枚举的大小(占用字节数)
虽然枚举常量是整数,但在内存中,枚举变量的实际大小取决于编译器实现,通常:
- 枚举变量在大多数编译器中被当作
int
处理,占 4个字节 - 但有些嵌入式平台可能会优化为更小的大小(如 1 或 2 字节),如果值范围允许的话
你可以用 sizeof()
查看:
#include <stdio.h>
typedef enum {
A, B, C
} MyEnum;
int main() {
printf("Size of enum: %zu bytes\n", sizeof(MyEnum)); // 通常输出 4
return 0;
}
二、结构体
1.结构体的基本语法
在 C 语言中,结构体的定义方式如下:
struct [结构体标签] {
成员列表;
};
其中 [结构体标签]
是可选的。根据是否使用这个标签,可以分为以下几种情况:
2.命名结构体(Named Struct)
先定义结构体类型,再声明变量
struct Point {
int x;
int y;
};
struct Point p1, p2;
定义结构体类型的同时声明变量
struct Point {
int x;
int y;
} p1, p2;
struct Point{ int x; int y;} p1;
int x;
- 不要混淆结构体和结构体变量
Point
是结构体的“名字”或“标签”- 后续可以用
struct Point
来声明变量、指针等 - 这是你所说的“先声明一个自定义 struct,然后才能用它去声明其他名称的结构体”的含义
✅ 这种方式适合多次使用该结构体类型的情况。
3.匿名结构体(Unnamed Struct)
情况1:只定义结构体,不给标签名
struct {
int x;
int y;
} point1, point2;
- 这是一个 匿名结构体
point1
和point2
是这个结构体类型的两个变量- 不能再次声明同类型的变量,因为没有标签名
- 一般用于一次性使用的场景
⚠️ 注意:下面这句是错误的!
struct point3; // 错误!因为没有标签名为 "point" 的结构体
情况2:嵌套在另一个结构体中的匿名结构体
struct Person {
char name[50];
struct { // 匿名结构体
int day;
int month;
int year;
} birthday;
} person1;
- 这个内部结构体没有名字
- 只能在
Person
结构体里访问它的成员:person1.birthday.year
- 不能单独声明这种类型的变量
4.使用 typedef 给结构体起别名(最推荐做法)
你可以结合 typedef
把结构体变成一个真正的“新类型名”,简化写法:
typedef struct {
int x;
int y;
} Point;
Point p1, p2;
- 不需要再写
struct Point
- 类型名直接是
Point
- 更加简洁,也更容易维护
也可以这样写:
typedef struct Point {
int x;
int y;
} Point;
这样可以同时拥有:
- 标签名
struct Point
- 类型名
Point
✅ 总结
如果你想多次使用同一个结构体类型,就必须给它一个名字(标签),否则就是匿名结构体,只能用一次。
补充说明:为什么不能重复使用匿名结构体?
比如:
struct {
int a;
int b;
} s1;
struct s2; // ❌ 错误!没有叫 "s2" 的结构体标签
因为你没有给结构体起标签名,所以编译器无法知道你要引用哪个结构体。
场景 | 推荐做法 |
---|---|
需要多次使用同一结构体 | 使用命名结构体或配合 typedef |
只需临时使用一次结构体 | 可以使用匿名结构体 |
在结构体中嵌套子结构体 | 如果需要外部访问,最好给标签;否则可以匿名 |
三、类型定义
typedef
是 C 语言中的一个关键字,用于 为已有数据类型创建一个新的名称(别名)。它的主要作用是:
提高代码可读性
简化复杂类型的声明
增强代码的可移植性
✅ 本质:
typedef
不会创建新的数据类型,只是给已有的类型起个别名。基本语法
typedef 已有类型名 新类型名;
示例:
// 为基本类型起别名 typedef int MyInt; MyInt a = 10;
// 为结构体起别名(最常见用法) typedef struct { float x; float y; float z; } PointCoord_def; PointCoord_def Gt_point;
// 为枚举起别名 typedef enum { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY } Day_ENUM; Day_ENUM Ge_today = FRIDAY;
/* 函数指针的别名 函数指针允许你动态地决定要执行哪个函数,这在需要灵活处理不同类型的任务时非常有用。 结合 typedef 和函数指针,你可以创建出既高效又易于维护的代码结构,特别是在设计库或框架时尤为有用。回调函数,钩子函数,使用C去模仿C++ 类(Class)里面的方法,事件总线 */ typedef int (*FuncPtr)(int, int); // FuncPtr 是一种指向“接受两个 int,返回一个 int”的函数的指针类型 // 现在,FuncPtr 成为了一个新的类型名称,它可以用于声明指向具有相同签名(即相同的参数列表和返回类型)的任何函数的指针。 int add(int a, int b) { return a + b; } int main() { FuncPtr fp = &add; printf("%d\n", fp(3, 4)); // 输出 7 return 0; }
// 为数组类型起别名(少见但有用) // 使用场景较少,但在某些嵌入式或固定大小数据结构中很有用。 typedef int MyArray[10]; MyArray arr; // arr 是一个包含 10 个 int 的数组
番外:数据结构
OLED/LCD菜单 展示