首页 > 其他分享 >2024-02-18-物联网C语言(8-结构体、共用体、枚举)

2024-02-18-物联网C语言(8-结构体、共用体、枚举)

时间:2024-02-19 14:45:50浏览次数:29  
标签:02 char struct int 18 2024 字节 结构 变量

8. 结构体、共用体、枚举

8.1 结构体的概念和定义

8.1.1 基本概述

构造类型:
不是基本类型的数据结构也不是指针,它是若干个相同或不同类型的数据构成的集合。常用的构造类型有数组结构体共用体

  • 数组用于保存多个相同类型的数据
  • 结构体用于保存多个不同类型的数据

8.1.2 概念

结构体是一种构造类型的数据结构,是一种或多种基本类型或构造类型的数据的集合。

8.1.3 定义

struct  结构体类型名{
    成员列表
}
// 第一种定义方法
struct stu{           // 定义三个struct stu成员
    int num;
    char name[20];
    char sex;
};

// 有了结构体之后,可以用类型定义变量
struct stu lucy, bob , meimei; // 每个变量都有三个成员 num,name,sex
// 第二种定义方法
struct 结构体类型名{
    结构体变量
} 结构体变量1,结构体变量2;


struct stu{
    int num;
    char name[20];
    char sex;
} lucy,bob,lilei; // 定义结构体的同时定义结构体变量

struct stu xiaohong,xiaoming; // 这种就和第一种一样,定义完类型后再定义变量

注意:

  1. 一般结构体类型都会定义在全局,也就是main函数的外面。所以在定义结构体类型的同时定义变量,这些变量一般都是全局变量
  2. 定义完类型之后,再定义的结构体变量,内存分配要看定义的位置
// 第三种定义方法,无名结构体

struct {
    成员列表
} 变量1,变量2;


// 注意:无名结构体由于没有结构体名,所以定义完之后是无法在定义结构体变量的,只能在定义类型的同时定义结构体变量
// 第四种方法,最常用的方法
typedef struct 结构体名{
       成员列表
} 重新定义的结构体类型名A;

// 1. 先用结构体定义变量
typedef struct stu{
    int num;
    char name[20];
    char sex;
} STU;

// 注意:typedef主要用于给一个类型取别名,此时相当于给当前结构体重新起了一个类型名为A相当于 struct 结构体名,所以如果结构体要取别名,一般不需要先给结构体定义名字,定义结构体变量时,直接使用A即可,不用加struct

// 2. 使用
STU lucy; // 这个与struct stu lucy是等价的

8.2 结构体变量的初始化

#include <stdio.h>

struct stu
{
    int id;
    char name[32];
    char sex;
    int age;
} zhangsan, lisi;

// 使用typedef对结构体取别名
typedef struct 
{
    int a;
    int b;
} MSG;


int main(int argc, char const *argv[])
{
    struct stu wangwu;
    struct stu zhaoliu = {1001,"老刘", "b",20};
    
    // typedef 定义变量不需要加struct
    MSG msg1,msg2;
    return 0;
}

8.3 结构体变量的使用

8.3.1 基本调用

结构体变量对成员的调用方式:

结构体变量.结构体成员

注意:这地方的结构体变量针对的是普通结构体变量

#include <stdio.h>
#include <string.h>

struct stu
{
    int id;
    char name[32];
    char sex;
    int age;
} zhangsan, lisi;

// 使用typedef对结构体取别名
typedef struct
{
    int a;
    int b;
} MSG;

int main(int argc, char const *argv[])
{
    struct stu wangwu;
    struct stu zhaoliu = {1001, "老刘", "b", 20};

    // typedef 定义变量不需要加struct
    MSG msg1, msg2;

    // 结构体变量使用
    zhangsan.id = 1000;
    strcpy(zhangsan.name, "zhangsan"); // 字符串赋值
    zhangsan.sex = 'A';
    zhangsan.age = 18;
    printf("%d - %s -%c -%d\n", zhangsan.id, zhangsan.name, zhangsan.sex, zhangsan.age);
    return 0;
}

输出结果

1000 - zhangsan -A -18

8.3.2 结构体嵌套

#include <stdio.h>
#include <string.h>

typedef struct
{
    int year;
    int month;
    int day;
} BD;

typedef struct stu
{
    int id;
    char name[32];
    BD birthday;

} STU;

int main(int argc, char const *argv[])
{
    STU xiaoming;
    xiaoming.id = 1000;
    strcpy(xiaoming.name, "xiaoming");
    xiaoming.birthday.year = 2022;
    xiaoming.birthday.month = 12;
    xiaoming.birthday.day = 10;
    printf("%d - %s - %d - %d -%d", xiaoming.id, xiaoming.name, xiaoming.birthday.year, xiaoming.birthday.month, xiaoming.birthday.day);
    return 0;
}

输出结果

1000 - xiaoming - 2022 - 12 -10

8.3.3 结构体相互赋值

相同类型的结构体变量可以相互赋值

#include <stdio.h>
#include <string.h>

struct stu
{
    int id;
    char name[32];
    char sex;
    int age;
} zhangsan;

int main(int argc, char const *argv[])
{
    zhangsan.id = 1000;
    strcpy(zhangsan.name, "zhangsan");
    zhangsan.sex = 'B';
    zhangsan.age = 18;
    printf("%d - %s -%c -%d\n", zhangsan.id, zhangsan.name, zhangsan.sex, zhangsan.age);

    // 相同类型的结构体之间可以相互赋值
    struct stu lisi;

    lisi = zhangsan;
    strcpy(lisi.name, "lisi");
    printf("%d - %s -%c -%d\n", lisi.id, lisi.name, lisi.sex, lisi.age);
    return 0;
}

输出结果

1000 - zhangsan -B -18
1000 - lisi -B -18

8.4 结构体数组

结构体数组是一个数组,由若干个相同类型的结构体变量构成的集合

8.4.1 结构体数组的定义方法

struct 结构体类型名 数组名[元素个数];

struct stu edu[3]; // 定义了一个struct stu类型的结构体数组stu
    

8.4.2 引用

// 使用下标引用
// 数组名[下标]
edu[1]

8.4.3 结构体数组元素对成员的使用

数组名[下标].成员
#include <stdio.h>

typedef struct
{
    /* data */
    int num;
    char name[20];
    float score;
} STU;

int main(int argc, char const *argv[])
{
    /* 定义一个结构体数组 */
    STU edu[3] = {{101, "Lucy", 78},
                  {102, "Bob", 59.5},
                  {103, "Tom", 85}};

    int i;
    float sum = 0;
    for (i = 0; i < 3; i++)
    {
        sum += edu[i].score;
    }
    printf("平均成绩为%.2f", sum / 3);
    return 0;
}

输出结果

平均成绩为74.17

8.5 结构体指针

结构体指针即结构体的地址,结构体变量存放内存中,也有起始地址。

定义一个变量来存放这个地址,那这个变量就是结构体指针变量。

struct 结构体类型名 * 结构体指针变量名;

// 结构体指针变量对成员的引用
(*结构体指针变量名).成员;
结构体指针变量名 -> 成员;  // 这种方式最常用
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct stu
{
    /* data */
    int id;
    char name[32];
    char sex;
    int age;
};

int main(int argc, char const *argv[])
{
    // 定义一个结构体指针变量
    struct stu *s;
    
    // 在堆区开辟结构体空间并将其地址保存在结构体指针变量中
    s = (struct stu *)malloc(sizeof(struct stu));

    s->id = 1001;
    strcpy(s->name, "zhangsan");
    s->sex = 'B';
    s->age = 20;
    printf("%d - %s - %c -%d\n", s->id, s->name, s->sex, s->age);

    return 0;
}

输出结果

1001 - zhangsan - B -20

8.6 结构体内存分配

8.6.1 结构体内存分配

? 结构体变量大小是它所有成员之和。因为结构体变量是所有成员的集合。

貌似这两句话没啥问题,实际上不是这样的,C语言中开辟内存是有规则的。

8.6.2 规则 1: 以多少个字节为单位开辟内存

​ 在结构体变量分配内存的时候,会在结构体变量中找占字节数多的基本类型成员,就以它大大小为单位开辟内存。(在 gcc 中出现了 double 类型的例外)

  1. 成员中只有 char 型数据 ,以1字节为单位开辟内存。

  2. 成员中出现了 short int 类型数据,没有更大字节数的基本类型数据,以2字节为单位开辟内存

  3. 成员中出现了 int float且 没有更大字节的基本类型数据的时候以4字节为单位开辟内存

  4. 成员中出现了 double 类型的数据,以8字节为单位开辟内存

    在vc环境,double以8字节开辟内存空间;
    在gcc环境,double以4字节开辟内存空间;
    //注意 :上述是针对结构体而言的,对于变量,无论哪种环境都是8字节
    
  5. 如果在结构体中出现了数组,数组可以看成多个变量的集合。
    如果出现指针的话,没有占字节数更大的类型的,以4字节为单位开辟内存。

8.6.3 规则 2: 字节对齐

  1. char 1字节对齐 ,即存放 char 型的变量,内存单元的编号是1的倍数即可。
  2. short int 2 字节对齐 ,即存放 short int 型的变量,起始内存单元的编号是2的倍数即可。
  3. int 4 字节对齐 ,即存放 int 型的变量,起始内存单元的编号是4的倍数即可
  4. long int 在 32 位平台下,4 字节对齐 ,即存放 long int 型的变量,起始内存单元的编号是4的倍数即可
  5. float 4字节对齐,即存放foat 型的变量,起始内存单元的编号是4的倍数即可
  6. double
    a. vc环境下
    8字节 对齐,即存放double型变量的起始地址,必须是8的倍数;double变量占8字节
    b.gcc环境下
    4字节对齐,即存放double型变量的起始地址,必须是4的倍数;double变量占8字节
struct stu{
    char sex;
    int age;
} lucy;
// lucy 的内存空间是 4字节的倍数
  1. 如果在结构体中出现了数组,可以看成多个变量的集合
  2. 开辟内存空间的时候,从上向下依次按成员在结构体中的位置顺序开辟空间
#include <stdio.h>

struct stu
{
    char a;
    short b;
    int c;

}temp;

int main(int argc, char const *argv[])
{
    printf("%d \n",sizeof(temp));
    printf("%p \n",&(temp.a));
    printf("%p \n",&(temp.b));
    printf("%p \n",&(temp.c));
    return 0;
}

输出结果

// 最大成员是int类型,那么结构体空间是4字节的倍数
8 
00007ff6c824a030
00007ff6c824a032
00007ff6c824a034
struct stu{
    char buf[10];  // 占用12字节
    int a; // 占用4个字节
}temp;
// 最大成员是int类型,那么结构体空间是4字节的倍数,temp占用16个字节
// 含double 变量的结构体
#include <stdio.h>

struct stu
{
    char a;
    double b;
}temp;

int main(int argc, char const *argv[])
{
    printf("%d \n",sizeof(temp));
    printf("%p \n",&(temp.a));
    printf("%p \n",&(temp.b));
    return 0;
}

输出结果

// 在vc中占 16 个字节 a和b的地址差8个字节 
// 在gcc中占 12个字节 a和b的地址差4个字节

16 
00007ff6c1a6a030
00007ff6c1a6a038

8.6.4 为什么需要字节对齐?

用空间换时间,提高cpu读取数据的效率

8.7 位段

在结构体中,以位为单位的成员,称之为位段(或者位域)

struct packed data
{
    unsigned int a:2;
    unsigned int b:6;
    unsigned int c:4;
    unsigned int d;4;
    unsigned int i;
} data;

注意:不能对位段成员取地址

  1. 位段引用

    data.a = 2; //赋值不要超过位段定义的范围
    
  2. 位段成员的类型必须指定为整型或者字符型

  3. 一个位段必须存放在一个存储单元中,不能跨两个单元

    第一个单元空间不能容纳下一个位段,则该空间不可再用,而是从下一个单位开始存储。

  4. 位段存储单元

    位段的存储单元:
    (1):char 型位段 存储单元是1个字节
    (2):short int 型的位段存储单元是2个字节
    (3):int 的位段存储单元是4字节
    (4):1ong int 的位段,存储单元是4字节
    
    #include <stdio.h>
    
    struct stu{
        char a:7;
        char b:7;
        char c:2;
    }temp;       // 占用三个字节,b不能跨其存储单元存储
    int main(){
        printf("%d\n",sizeof(temp));
        return 0
    }
    
  5. 位段长度不能大于存储单元的长度

    (1):char 型位段不能大于 8 位;
    (2):short int 型位段不能大于 16 位;
    (3):int 的位段不能大于 32 位;
    (4):long int 的位段,位段不能大于 32 位
    
    #include<stdio.h>
    
    struct stu{
        char a:9;  // char存储单元的大小为8,这儿定义时是9,会出错
        char b:7;
        char c:2;
    }temp;
    
    int main{
        printf("%d\n",sizeof(temp));  // 编译出错,位段a不能大于其存储单元大小
        return 0;
    }
    
  6. 如果一个位段需要从另一个存储单元开始,可以定义

    unsigned char a:1;
    unsigned char b:2; 
    unsigned char :0;  // 由于用了长度为 0 的位段,其作用是使下一个位段从下一个存储单元开始存放
    unsigned char c:3; // (另一个单元)
    
  7. 可以定义无意义的位段

    unsigned a:1;
    unsigned :2; // 无意义的位段
    unsigned b:3;
    

8.8 共用体 - union

共用体和结构体类似,也是一种构造类型的数据结构。
定义共用体类型的方法和结构体非常相似,把 stuct 改成 union 就可以了。

// 在进行某些算法的时候,需要使几种不同类型的变量存到同一段内存单元中,几个变量所使用空间相互重叠,这种几个不同的变量共同占用一段内存的结构,在C语言中,被称作“共用体”类型结构
  1. 共用体所有成员占有同一段地址空间
  2. 共用体的大小是 其占内存长度最大的成员的大小
typedef struct data{
    short int i;
    char ch;
    float f;
}DATA;
DATA temp1;  // 结构体变量中,temp1最小占用7个字节(2+1+4)

typedef union data{
    short int i;
    char ch;
    float f;
}DATA;
DATA temp2;  // 共有体变量中,temp1最小占用4个字节(float最大,为4),这个空间 i,ch,f共同使用

共用体的特点:

  1. 同一内存段可以用来存放几种不同类型的成员,但每一瞬时只有一种起作用
  2. 共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖
  3. 共用体变量的地址和它的各成员的地址都是同一地址
#include <stdio.h>

union un
{
    int a;
    int b;
    int c;
};

int main(int argc, char const *argv[])
{
    union un myun;
    myun.a = 100;
    myun.b = 200;
    myun.c = 300;
    printf("a = %d ,b = %d, c= %d", myun.a, myun.b, myun.c);

    return 0;
}

输出结果

a = 300 ,b = 300, c= 300

8.9 枚举 - enum

将变量的值一一列举出来,变量的值只限于列举出来的值的范围内

枚举类型也是个构造类型的

8.9.1 枚举定义

enum 枚举类型名{
    枚举列举值;  // 在枚举值列表中列出所有可用值,也称为枚举元素
}
// 枚举变量仅能去枚举值所列的元素
enum week{
    Mon,Tue,Wed,Thur,Fri,Sat,Sun
}

enum week workday,weekday; // workday ,weekday只能去Mon ... Sun中间的一个
workday = Mon; // 正确
weekday = Tue; // 正确
weekday = abc; // 错误

8.9.2 枚举特点

  1. 枚举值是常量,不能在程序中用赋值语句再对它赋值

    例如:sun=5; mon=2; sun=mon; 都是错误的
    
  2. 枚举元素本身由系统定义了一个表示序号的数值,默认是从0开始顺序定义为0,1,2..

    如在week中,mon值为0,tue值为1,...,sun值为6
    
  3. 可以改变枚举值的默认值

    enum week{ 			//枚举类型
        mon=3 , tue , wed , thu , fri=4 , sat,sun
    }
    
    // mon=3 tue=4,以此类推
    // fri=4 sat=5 sun=6,以此类推
    

    注意:在定义枚举类型的时候枚举元素可以用等号给它赋值,用来代表元素从几开始编号

    在程序中,不能再次对枚举元素赋值,因为枚举元素是常量

标签:02,char,struct,int,18,2024,字节,结构,变量
From: https://www.cnblogs.com/hasaki-yasuo/p/18021059

相关文章

  • 【2024】docker overlay2 迁移
    参考了很多文章,docker起不起来。报如下错误:docker.service:ServicehasmorethanoneExecStart=setting,whichisonlyallowedforType=oneshotservices.Refusing.关键是新的docker路径的配置方法不适用。参考官网 https://docs.docker.com/config/daemon/编辑da......
  • 前端知识回顾概览--React 17+18
    react目前最火的前端框架之一状态管理、路由等一定要重点掌握熟悉常见API,并且有使用经验1.react.js基础react.js简介jsx模版语法及babel编译配置事件/条件渲染/列表渲染等基础用法react.js组件化及生命周期refs及ReactAPI详解create-react-appcli的......
  • 微软 Office 2021 专业增强版,安装完自动激活
    123盘下载地址  MicrosoftOffice2021VL官方版下载丨最新版下载丨绿色版下载丨APP下载-123云盘(123pan.com)安装前先关闭windows系统自带的 病毒  微软办公软件套件MicrosoftOfficeLTSC2021专业增强版2024年01月批量许可版更新推送!Office2021正式版和Windows11......
  • 海亮02/19杂题
    海亮02/19杂题个人题单T5link题意设一个数组\(a_{1,2,\dots,l}\)是平衡的,当且仅当\(\existsk\in[1,\frac{l-1}{2}],\foralli\in[1,l-2\timesk],a_{i}+a_{i+2\timesk}=2\timesa_{i+k}\)。现在给你一个数组\(a\),你需要对\(\foralll\in[1,n]\)求出子序列......
  • 2024/1/18
    查找某元素的下标功能:查找指定元素在列表的下标,如果找不到,报错ValueError语法:列表.index(元素)index就是列表对象(变量)内置的方法(函数)插入元素:语法:列表[inser(下标,元秦),在指定的下标位置,插入指定的元素 my_list=[1,2,3]my_list.insert(1,"itheima"print(my_list)#结果:[......
  • 2024/1/20
    while循环和for循环,都是循环语句,但细节不同:在循环控制上:while循环可以自定循环条件,并自行控制for循环不可以自定循环条件,只可以一个个从容器内取出数据在无限循环上:while循环可以通过条件控制做到无限循环for循环理论上不可以,因为被遍历的容器容量不是无限的在使用场景上:whi......
  • 2024/1/13
     注释的分类单行注释:以#开头,#右边的所有文字当作说明,而不是真正要执行的程序,起辅助说明作用#我是单行注释print("Helloworld")注意,#号和注释内容一般建议以一个空格隔开多行注释:以一对三个双引号引起来("""注释内容""")来解释说明一段代码的作用使用方法type()语句的使用......
  • 2021/1/14
    可以看出,for循环是将字符串的内容:依次取出所以,for循环也被称之为:遍历循环 同while循环不同,for循环是无法定义循环条件的只能从被处理的数据集中,依次取出内容进行处理所以,理论上讲,Python的for循环无法构建无限循环(被处理的数据集不可能无限大 range语句语法1:range(nu......
  • 2024/1/15
    1.什么是NoneNone是类型'NoneType’的字面量用于表示:空的、无意义的2.函数如何返回None不使用return语句即返回None主动returnNone3.使用场景函数返回值if判断变量定义 ......
  • 2024/1/16
    函数是纯代码语言,想要理解其含义,就需要一行行的去阅读理解代码,效率比较低。我们可以给函数添加说明文档,辅助理解函数的作用语法如下:deffunc(x,y):"""函数说明:paramx:形参x的说明:paramy:形参y的说明:return :返回值的说明"""函数体return返回值......