C语言结构体全面解析:从基础定义到高级应用

C语言结构体:从基础到嵌入式应用

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
}

这种用法在嵌入式开发中非常常见,可以方便地按位或按字节访问数据

六、结构体使用注意事项

  1. 内存对齐:结构体成员在内存中可能不是紧凑排列的,编译器会进行对齐优化。可以使用#pragma pack控制对齐方式

  2. 大小端问题:在跨平台或网络传输时,需要注意结构体内存布局的大小端问题

  3. 深浅拷贝:结构体赋值是浅拷贝,对于包含指针的成员需要手动实现深拷贝

  4. 初始化:结构体可以整体初始化,但不能整体赋值(C语言中)

  5. 作为函数返回值:返回大型结构体会产生拷贝开销,通常返回指针更高效

结构体是C语言中组织复杂数据的强大工具,在嵌入式开发中尤其重要。合理使用结构体可以使代码更加模块化、可读性更强,如用户代码中的cpu_2_mcu_data_t结构体数组就很好地组织了CPU与MCU之间的通信数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值