C语言结构体全面解析:从基础定义到高级应用
一、结构体的基本定义与使用
结构体(struct)是C语言中一种自定义的复合数据类型,它允许将不同类型的数据组合在一起形成一个新的数据类型。结构体的定义方式有多种形式:
1. 基本定义方式
// 方式1:先定义结构体类型,后定义变量
struct Student {
char name[20];
int age;
float score;
};
struct Student stu1, stu2;
// 方式2:定义类型的同时定义变量
struct Student {
char name[20];
int age;
float score;
} stu1, stu2;
// 方式3:匿名结构体直接定义变量(无法复用)
struct {
char name[20];
int age;
float score;
} stu1, stu2;
2. 使用typedef简化
typedef可以为结构体创建别名,简化代码:
// 方式1:先定义结构体再typedef
struct Student {
char name[20];
int age;
float score;
};
typedef struct Student Student_t;
// 方式2:定义时直接typedef
typedef struct Student {
char name[20];
int age;
float score;
} Student_t;
// 方式3:匿名结构体typedef
typedef struct {
char name[20];
int age;
float score;
} Student_t;
使用typedef后,定义变量时不再需要写struct关键字:Student_t stu1;
二、结构体数组的定义与使用
结构体数组允许我们创建多个相同结构体类型的变量集合,如cpu_2_mcu_data_t tmp_rcvd_cpu_pkt[100];就是定义了一个包含100个结构体元素的数组。
1. 结构体数组定义方式
// 方式1:先定义结构体类型,后定义数组
struct Student {
char name[20];
int age;
};
struct Student students[100];
// 方式2:定义类型的同时定义数组
struct Student {
char name[20];
int age;
} students[100];
// 方式3:匿名结构体直接定义数组
struct {
char name[20];
int age;
} students[100];
2. 结构体数组初始化
// 完全初始化
struct Student students[3] = {
{"张三", 18, 90.5},
{"李四", 19, 85.0},
{"王五", 20, 92.5}
};
// 部分初始化(未初始化的元素成员自动置0)
struct Student students[3] = {
{"张三", 18},
{"李四"} // age和score默认为0
};
// C99指定初始化(可乱序)
struct Student students[3] = {
{.name="张三", .age=18},
{.score=85.0, .name="李四"}
};
3. 结构体数组访问
// 通过下标访问
strcpy(students[0].name, "赵六");
students[1].age = 21;
// 通过指针遍历
struct Student *p = students;
for(int i=0; i<3; i++) {
printf("Name: %s, Age: %d\n", p->name, p->age);
p++;
}
结构体数组在嵌入式开发中常用于批量管理设备信息、协议数据包缓存等场景,如用户代码中的tmp_rcvd_cpu_pkt[100]可能用于存储从CPU接收的100个数据包
三、结构体指针与内存管理
1. 结构体指针基础
typedef struct {
int id;
char name[20];
} Employee;
Employee emp1 = {1, "张三"};
Employee *pEmp = &emp1;
// 通过指针访问成员
printf("ID: %d, Name: %s\n", pEmp->id, pEmp->name);
2. 动态内存分配
// 单个结构体
Employee *pEmp = (Employee*)malloc(sizeof(Employee));
pEmp->id = 2;
strcpy(pEmp->name, "李四");
free(pEmp);
// 结构体数组
Employee *pEmps = (Employee*)malloc(10 * sizeof(Employee));
for(int i=0; i<10; i++) {
pEmps[i].id = i+1;
sprintf(pEmps[i].name, "员工%d", i+1);
}
free(pEmps);
3. 结构体指针作为函数参数
void printEmployee(const Employee *emp) {
printf("ID: %d, Name: %s\n", emp->id, emp->name);
}
void modifyEmployee(Employee *emp, int newId, const char *newName) {
emp->id = newId;
strcpy(emp->name, newName);
}
使用指针传递结构体比传值更高效,特别是对于大型结构体
四、结构体高级用法
1. 嵌套结构体
typedef struct {
char street[50];
char city[30];
int zip;
} Address;
typedef struct {
int id;
char name[30];
Address addr; // 嵌套结构体
double salary;
} Employee;
Employee emp = {
1,
"张三",
{"人民路123号", "北京市", 100000},
8000.0
};
2. 结构体包含数组
typedef struct {
int id;
char name[20];
float scores[5]; // 数组成员
} Student;
Student stu = {
1001,
"李四",
{90.5, 85.0, 92.5, 88.0, 95.5}
};
3. 柔性数组(C99特性)
typedef struct {
int length;
char data[]; // 柔性数组(必须是最后一个成员)
} FlexArray;
FlexArray *createFlexArray(int size) {
FlexArray *fa = malloc(sizeof(FlexArray) + size * sizeof(char));
fa->length = size;
return fa;
}
柔性数组常用于变长数据结构的实现
五、结构体在嵌入式开发中的应用
1. 寄存器映射
typedef struct {
volatile uint32_t CR; // 控制寄存器
volatile uint32_t SR; // 状态寄存器
volatile uint32_t DR; // 数据寄存器
volatile uint32_t IER; // 中断使能寄存器
} USART_TypeDef;
#define USART1 ((USART_TypeDef *)0x40011000)
void USART_Init() {
USART1->CR = 0x2000; // 使能USART
USART1->IER = 0x01; // 使能接收中断
}
2. 协议数据包处理
typedef struct {
uint8_t header; // 帧头
uint8_t cmd; // 命令字
uint8_t length; // 数据长度
uint8_t data[32]; // 数据域
uint16_t checksum; // 校验和
} ProtocolPacket;
ProtocolPacket pkt;
uart_receive(&pkt, sizeof(ProtocolPacket));
if(pkt.header == 0xAA && verify_checksum(&pkt)) {
process_packet(&pkt);
}
3. 与共用体结合处理数据
typedef union {
struct {
uint8_t b0:1;
uint8_t b1:1;
uint8_t b2:1;
uint8_t b3:1;
uint8_t b4:1;
uint8_t b5:1;
uint8_t b6:1;
uint8_t b7:1;
} bits;
uint8_t byte;
} ByteToBits;
ByteToBits flags;
flags.byte = 0xA5;
if(flags.bits.b0) {
// 处理bit0
}
这种用法在嵌入式开发中非常常见,可以方便地按位或按字节访问数据
六、结构体使用注意事项
-
内存对齐:结构体成员在内存中可能不是紧凑排列的,编译器会进行对齐优化。可以使用
#pragma pack控制对齐方式 -
大小端问题:在跨平台或网络传输时,需要注意结构体内存布局的大小端问题
-
深浅拷贝:结构体赋值是浅拷贝,对于包含指针的成员需要手动实现深拷贝
-
初始化:结构体可以整体初始化,但不能整体赋值(C语言中)
-
作为函数返回值:返回大型结构体会产生拷贝开销,通常返回指针更高效
结构体是C语言中组织复杂数据的强大工具,在嵌入式开发中尤其重要。合理使用结构体可以使代码更加模块化、可读性更强,如用户代码中的cpu_2_mcu_data_t结构体数组就很好地组织了CPU与MCU之间的通信数据
C语言结构体:从基础到嵌入式应用
1140

被折叠的 条评论
为什么被折叠?



