嵌入式学习Day19

一、结构体的定义:自定义数据类型

结构体是一种构造数据类型,我们可以根据需求定义包含多个不同类型成员的新类型。

语法模板

struct 类型名
{
    数据类型 成员变量名1;
    数据类型 成员变量名2;
    数据类型 成员变量名3;
    // ... 更多成员
};
// 定义日期结构体
struct date
{
    int year;
    int mon;
    int day;
};

// 定义时间结构体
struct time
{
    int hour;
    int min;
    int sec;
};

// 定义学生结构体
struct Stu
{
    char name[50];  // 姓名
    int age;        // 年龄
    char sex;       // 性别
    int score;      // 成绩
};

关键点:结构体定义只是创建了一个 “数据模板”,不会分配内存,只有定义变量时才会占用内存。

二、定义结构体变量:基于模板创建实例

定义结构体变量的方式和普通变量类似,只是需要带上struct关键字和结构体名:

// 普通变量定义
int a;

// 结构体变量定义
struct 结构体名 变量名;
struct Stu zhangsan;  // 定义一个学生变量zhangsan

三、结构体初始化:给变量赋初始值

结构体支持多种初始化方式,根据需求选择即可:

1. 全部初始化

按成员顺序一次性赋值:

struct Stu s = {"zhangsan",20,'m',90};

2. 局部初始化(GNU GCC 编译器支持)

可以只给部分成员赋值,未赋值的成员会被初始化为 0:

struct Stu s2 = {
    .name="zhangsan",
    .score = 80,
};

3. 初始化为全 0

快速将所有成员置为 0:

struct Stu s3={0};

四、结构体成员的访问:两种方式

访问结构体成员有两种常用写法,取决于你是直接操作变量还是通过指针操作。

1. 变量访问:使用 . 运算符

struct Stu s = {"zhangsan",20,'m',90};
s.age = 22;  // 直接修改年龄
strcpy(s.name,"zhang123");  // 字符串赋值需要用strcpy,不能直接赋值数组名
printf("name:%s age:%d sex:%c score:%d\n",s.name,s.age,s.sex,s.score);

2. 指针访问:使用 -> 运算符

struct Stu *p = &s;  // 定义指向结构体的指针
printf("name:%s age:%d sex:%c score:%d\n",p->name,p->age,p->sex,p->score);

五、结构体的存储:字节对齐问题

结构体的内存布局不是简单的成员大小相加,而是会遵循字节对齐规则,这是为了让 CPU 更高效地访问数据。

对齐规则

  1. 成员对齐:结构体的每个成员必须存放在 “自身类型大小的整数倍” 地址上。例如:int类型成员的地址必须是 4 的倍数,double类型成员的地址必须是 8 的倍数。
  2. 整体对齐:结构体的总大小必须是 “最大成员类型大小” 的整数倍。

示例:计算结构体大小

struct Stu
{
    char name[30];  // 30字节
    int age;        // 4字节
    short ID;       // 2字节
    double cost;    // 8字节
};
  • 理论大小:30+4+2+8=44
  • 实际大小:由于字节对齐,最终大小为48(最大成员是double,大小 8,48 是 8 的整数倍)。

理解对齐:编译器会在成员之间填充空白字节,虽然浪费了空间,但提升了 CPU 的访问效率。

六、结构体参数的传递:推荐用指针

结构体作为函数参数时,有两种传递方式:

1. 值传递(不推荐)

会拷贝整个结构体,当结构体较大时,效率很低且浪费内存。

2. 地址传递(推荐)

传递结构体的指针,仅拷贝 8 字节(64 位系统)的地址,效率更高,且可以在函数内修改原结构体的内容。

实战示例

// 输入学生信息:用指针传递,修改原结构体
void input_stu(struct Stu* p)
{
    printf("input name:");
    gets(p->name);
    printf("input age:");
    scanf("%d",&p->age);
    getchar();  // 清除输入缓冲区的换行
    printf("input sex:");
    scanf("%c",&p->sex);
    printf("input score:");
    scanf("%lf",&p->score);
}

// 展示学生信息:用const修饰指针,保证不修改结构体
void show_stu(const struct Stu* p)
{
    printf("name:%s age:%d sex:%c score:%lf\n",p->name,p->age,p->sex,p->score);
}

int main()
{
    struct Stu s;
    input_stu(&s);  // 传递地址
    show_stu(&s);
    return 0;
}

七、结构体数组:批量管理结构体变量

当需要管理多个结构体实例(如多个学生)时,结构体数组是最佳选择。

定义与初始化

struct Stu a2[3]={
    {"zhangsan",1.77},
    {"lisi",1.65},
    {"wangmazi",1.22}
};

// 局部初始化(GNU GCC支持)
struct Stu a3[3]={
    [1]={
        .name="zhangfei",
    }
};

数组元素的操作

// 结构体变量之间可以直接复制
a[1] = a[0];

// 遍历数组
int i = 0 ;
for(i=0;i<3;i++)
{
    printf("id:%d name:%s height:%lf\n",i,a2[i].name,a2[i].heigh);
}

八、共用体(Union):节省内存的共享存储

共用体是一种为了节省内存而设计的自定义结构,也叫联合体,它的核心特点是所有成员共享同一块内存空间

1. 核心特性

  • 内存共享:结构体的成员内存是独立的,而共用体的所有成员共用同一块内存,修改一个成员会覆盖其他成员的值。
  • 节省内存:共用体的大小等于最大成员的大小,在早期内存紧张的计算机上非常实用。
  • 函数参数传递:与结构体类似,推荐使用指针传递,避免拷贝开销。

2. 定义语法

union 共用体名字
{
    数据类型 成员变量1;
    数据类型 成员变量2;
    数据类型 成员变量3;
    // ... 更多成员
};

3. 字节序关联知识点

在使用共用体处理多字节数据时,需要注意 CPU 的字节序:

  • 大端存储(如 51 单片机、网络协议):数据的低位存放在内存的高地址,高位存放在低地址。
  • 小端存储(如 ARM、Intel 处理器):数据的低位存放在内存的低地址,高位存放在高地址。

九、枚举类型(Enum):限定取值范围的常量集合

枚举是将变量的取值范围一一列举出来,变量只能在这个范围内取值,它本质是整型(int)常量的集合。

1. 核心特性

  • 常量集合:枚举出来的值是编译期确定的常量,不能在运行时修改。
  • 默认值规则:枚举值默认从 0 开始,后一个值在前一个值的基础上 + 1;也可以手动指定起始值。

2. 定义与使用

// 定义枚举类型:一周的星期
enum WEEK {MON,TUE,WED,THU,FRI,SAT,SUN};  // 默认MON=0,TUE=1...SUN=6
enum WEEK {MON=2,TUE,WED,THU,FRI,SAT,SUN};  // 手动指定MON=2,后续值依次+1

int main()
{
    enum WEEK w = FRI;  // 定义并初始化枚举变量
    w = SUN;  // 变量w的本质是int类型,可以赋值范围内的枚举值

    // 枚举变量非常适合用于switch分支
    switch(w)
    {
        case MON:
        case TUE:
        case WED:
            printf("好好学习\n");
            break;
        case THU:
        case FRI:
        case SAT:
            printf("放飞自我\n");
            break;
        case SUN:
            printf("放飞自我*2\n");
            break;
    }

    // 注意:C语言不强制检查枚举值范围,赋值超出范围不会报错,但逻辑上存在风险
    w = 100;  // GCC不会报警告,但这是一个潜在错误
    return 0;
}

十、位运算:直接操作二进制位的高效工具

位运算是直接对数据的二进制位进行操作,它的效率极高,常用于底层驱动、加密算法和性能优化场景。

1. 基础位运算符

运算符含义备注
&按位与:对应位都为 1 时结果为 1双目
``按位或:对应位有一个为 1 时结果为 1双目
^按位异或:对应位不同时结果为 1双目
~按位取反:0 变 1,1 变 0单目
<<逻辑左移:高位丢弃,低位补 0双目
>>逻辑右移:低位丢弃,高位补 0双目

2. 核心特点

  • 操作对象:仅支持整型数据(charintshortlong等)。
  • 高效性:位运算直接在 CPU 寄存器中完成,比算术运算快得多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值