从零开始学嵌入式技术之C语言12:自定义数据类型

一:枚举

        枚举(Enumeration)是C语言中的一种用户自定义数据类型,用于定义一组相关的常量。枚举使我们可以为这些常量分配有意义的名字,从而提高代码的可读性和可维护性。

        如果我们定义一个变量,用来表示一周中的一天,该变量的值只能从七个有效值中选择,枚举可以帮助我们限制变量的有效值。

(1)定义枚举

        枚举使用 enum 关键字来定义,枚举名称和枚举元素名称都是标识符,定义一个枚举就是定义了一种枚举数据类型,语法如下:

enum 枚举名称 
{
    枚举元素1,
    枚举元素2,
    ...
    枚举元素N
};

枚举元素也称为枚举成员或枚举常量,具有如下特点:

  1. 枚举元素的值必须在同一枚举中是唯一的
  2. 枚举元素的值必须是整数类型,通常是int
  3. 如果没有为枚举元素指定值,编译器会自动为它们分配值,从0开始,依次递增。
  4. 定义枚举的时候也可以为枚举元素自定义值,需保证唯一性和整型类型。

(2)枚举变量

        定义变量时指定类型是我们所定义的枚举类型,该变量称为枚举变量。枚举变量的值应该是枚举类型中的枚举常量中的一个,不能是其他值。

#include <stdio.h>
int main()
{
    // enum 枚举名称
    enum Week
    {
        // 枚举成员
        MONDAY,    // 周一
        TUESDAY,   // 周二
        WEDNESDAY, // 周三
        THURSDAY,  // 周四
        FRIDAY,    // 周五
        SATURDAY,  // 周六
        SUNDAY     // 周日
    } b;           // 第二种:定义出类型以后紧随定义枚举类型变量

    // 创建枚举类型的变量 第一种写法:先定义类型、在创建枚举变量
    enum Week a = MONDAY;
    // 枚举变量进行赋值
    a = SUNDAY;
    printf("枚举变量a=%d\n", a);

    // 定义枚举变量写法第三种
    // 定义枚举类型
    enum
    {
        SPRING = 3,
        SUMMER = 6,
        AUTUMN = 9,
        WINTER = 12
    } siji;
    siji = WINTER;
    printf("%d\n", siji);
    //第一种:定义枚举类型 在定义枚举变量
    //第二种:定义枚举类型的同时,定义枚举类型的变量
    //第三种:定义枚举类型的时候可以省略名字,同时定义枚举变量
    return 0;
}

 (3)枚举的应用

        如果枚举常量的值是连续的,我们可以使用循环遍历;如果枚举常量的值不是连续的,则无法遍历。

#include <stdio.h>
int main()
{
    // 定义枚举类型
    enum Week
    {
        MONDAY = 1, // 周一
        TUESDAY,    // 周二
        WEDNESDAY,  // 周三
        THURSDAY,   // 周四
        FRIDAY,     // 周五
        SATURDAY,   // 周六
        SUNDAY      // 周日
    };
    // 循环遍历枚举
    // 枚举在进行循环遍历的时候,成员的数值务必连续的,不是连续不能循环遍历!!!
    for (enum Week day = MONDAY; day <= SUNDAY; day++)
    {
        printf("今天是周%d\n", day);
    }

    return 0;
}

        枚举可以与switch结合使用,请看如下案例:

#include <stdio.h>
// 枚举
enum Season
{
    SPRING = 1, // 春天1
    SUMMER = 2, // 夏天2
    AUTUMN = 3, // 秋天3
    WINTER = 4  // 冬天4
};
int main()
{

    // 定义一个枚举变量
    enum Season season;
    printf("请您输入一个数字[1-4]:");
    scanf("%d", &season);
    // 条件判断
    switch (season)
    {
    case SPRING:
        printf("春天比较好,因为春天不冷!!!");
        break;
    case SUMMER:
        printf("夏天比较好,因为夏天不热!");
        break;
    case AUTUMN:
        printf("秋天比较好,因为不冷不热!");
        break;
    case WINTER:
        printf("冬天比较好,冬天能滑雪!!!!");
        break;
    default:
        printf("对不起,不在地球服务区内!");
        break;
    }

    return 0;
}

二:结构体

         C 语言内置的数据类型,除了几种原始的基本数据类型,只有数组属于复合类型,可以同时包含多个值,但是只能包含相同类型的数据,实际使用场景受限。

        C语言提供了struct关键字,允许自定义复合数据类型,将不同类型的值组合在一起,这种类型称为结构体(structure)类型。

        C语言没有其他语言的对象(object)和类(class)的概念,struct 结构很大程度上提供了对象和类的功能。

(1)声明结构体

格式如下:

struct 结构体名
{ 
    数据类型1 成员名1;   
    数据类型2 成员名2; 
    ……
    数据类型n 成员名n;
};

​​​​​​​声明结构体变量

#include <stdio.h>
// 需求:用结构体表示一个学生:编号、名字、性别(m|g)、手机号、分数、邮箱!!!!
int main()
{
    // 先定义结构体(自定义一种数据的类型)
    struct Stundets
    {
        // 数据类型  成员名字(标识符)
        int id;
        // 姓名
        char name[50];
        // 性别
        char sex;
        // 手机号
        char phone[12];
        // 分数
        double score;
        // 邮箱
        char email[20];
    } xiaohei;

    // 创建 结构体类型 的变量
    // 小明:自定义类型结构体 的变量(结构体变量)
    // 第一种写法:先定义结构体类型、在声明变量
    struct Stundets xiaoming;
    // 第二种写法:在结构体的后面直接声明变量

    // 第三种:狗结构体,省略结构体的名字
    struct
    {
        // 狗狗年龄
        int age;
        // 狗狗的名字
        char name[10];
        // 狗狗性别
        char sex;
    } erha;

    return 0;
}

        注意:在C语言中,结构体(struct)和结构体变量是两个不同的概念。

        结构体是一种自定义的数据类型,像一种模板,定义了数据的格式。

        结构体变量是根据结构体类型创建的变量,代表了一个具体的对象,用于存储数据。

(2)成员的获取和赋值

        成员是结构体的一个组成部分,一般是基本数据类型、也可以是数组、指针、结构体等。结构体的成员也可以称为属性。

        结构体和数组类似,也是一组数据的集合,结构体使用点号 . 获取单个成员,可以进行赋值和取值。

        结构体成员逐个赋值

#include <stdio.h>

int main()
{
    // 学员的结构体
    //  需求:用结构体表示一个学生:编号、名字、性别(m|g)、手机号、分数、邮箱!!!!
    struct Students
    {
        // 结构体的成员id、结构体的属性ID
        int id;
        // 名字
        char *name;
        // 性别
        char sex;
        // 手机号
        char *phone;
        // 分数
        double score;
        // 邮箱
        char *emial;
        // 考试等级
        char level[3];
    };
    // 创建结构体类型的变量
    struct Students lhy;
    // 成员进行赋值
    lhy.id = 1;
    lhy.name = "刘海洋";
    lhy.sex = 'm';
    lhy.phone = "17720125002";
    lhy.score = 59.5;
    lhy.emial = "110@163.com";
    // 点语法获取成员的数值
    printf("%s %lf %s\n", lhy.name, lhy.score, lhy.emial);
    // 点语法操作成员
    // 字符别写汉字
    // lhy.level[0] = 'a';
    // lhy.level[1] = 'b';
    // printf("%s", lhy.level);

    return 0;
}

        使用大括号一次性对结构体所有成员赋值 

#include <stdio.h>

int main()
{
    // // 结构体Students
    struct Students
    {
        // 编号
        int id;
        // 学生姓名
        char *name;
        // 学员的性别
        char sex;
        // 学员的手机号
        char *phone;
        // 学员的分数
        double score;
        // 学员的邮箱
        char *emial;
        char aaa[10];
    } wangqun = {1, "王群", 'm', "18866390611", 59.9, "abc@qq.com", "aaaaa"};

    // 访问成员的数据
    printf("编号:%d,姓名:%s,性别:%c,手机号:%s,分数:%lf,邮箱:%s\n", wangqun.id, wangqun.name, wangqun.sex, wangqun.phone, wangqun.score, wangqun.emial);

    return 0;
}

(3)结构体指针

        结构体指针是指向结构体的指针变量,允许间接访问和操作结构体的成员,它提供了一种方便的方式来处理结构体数据。

        声明结构体指针的语法格式:

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

         声明结构体指针

通常需要先定义一个结构体变量,再创建结构体指针,并取结构体变量的地址作为结构体指针的值。

#include <stdio.h>

int main()
{
    // 结构体
    struct Students
    {
        // 编号
        int id;
        // 名字
        char *name;
        // 性别
        char sex;
        // 手机号
        char *phone;
        // 邮箱
        char *email;
    };
    // 第一步:结构变量
    struct Students stu = {1, "程月", 'g', "110", "122@qq.com"};
    // 结构体变量操作成员用的是点点点点.语法
    printf("%s %d\n", stu.name, stu.id);
    // 结构体指针:操作成员,用的是箭头(右边箭头)
    struct Students *ptr = &stu;
    // 访问成员的数据
    ptr->name = "老哥";
    ptr->id = 5;
    // 指针操作结构体->
    printf("%s %d\n", ptr->name, ptr->id);
    (*ptr).id = 2;
    (*ptr).name = "大海";
    printf("%s %d\n", (*ptr).name, (*ptr).id);

    return 0;
}

(4)案列

其一:

  1. 编写一个Dog结构体,包含name(char *)、age(int)、weight(double)属性。
  2. 编写一个say函数,返回字符串,方法返回信息中包含所有成员值。
  3. 在main函数中,创建Dog结构体变量,调用say函数,将调用结果打印输出。
#include <stdio.h>
struct Dog
{
    // 成员
    char *name;
    int age;
    double weight;
};
char *say(struct Dog dog1)
{
    // 布局字符数据
    static char info[100];
    dog1.name = "泰迪";
    sprintf(info, "狗名:%s,年龄:%d,体重:%lf", dog1.name, dog1.age, dog1.weight);
    return info;
}
int main()
{
    // 定义结构体
    struct Dog dog = {"二哈", 18, 11.1};
    // 这里提前定义指针,在代码执行到这里的时候,指针指向的地址还不确定
    // 先指向NULL,别让str变为野指针
    char *str = NULL;
    str = say(dog);
    printf("%s", str);
    printf("dog===%s", dog.name);
    return 0;
}

其二:

  1. 编程创建一个Box结构体,在其中定义三个成员表示一个长方体的长、宽和高,长宽高可以通过控制台输入。
  2. 定义一个函数获取长方体的体积(volume)。
  3. 创建一个结构体指针,打印给定尺寸的长方体的体积。
#include <stdio.h>
struct Box
{
    // 长度
    double length;
    // 宽度
    double width;
    // 高度
    double height;
};
// 计算体积
double Volume(struct Box *ptr)
{
    ptr->height = 4;
    return ptr->length * ptr->width * ptr->height;
}
int main()
{
    // 结构体变量
    struct Box box;
    printf("请您输入长度:");
    scanf("%lf", &box.length);
    printf("请您输入宽度:");
    scanf("%lf", &box.width);
    printf("请您输入高度:");
    scanf("%lf", &box.height);
    double result = Volume(&box);
    printf("体积为%lf\n", result);
    //指针操作的还是box,因此15行代码影响到 box
    printf("高度:%lf到底是多少?\n", box.height);

    return 0;
}

其三:

景区门票案例

  1. 一个景区根据游人的年龄收取不同价格的门票。
  2. 请编写游人结构体(Visitor),根据年龄段决定能够购买的门票价格并输出。
  3. 规则:年龄>18,门票为20元,其它情况免费。
  4. 可以循环从控制台输入名字和年龄,打印门票收费情况,如果名字输入n,则退出程序。
#include <stdio.h>
#include <string.h>
/**
 * (1)一个景区根据游人的年龄收取不同价格的门票。
   (2)请编写游人结构体(Visitor),根据年龄段决定能够购买的门票价格并输出。
   (3)规则:年龄>18,门票为20元,其它情况免费。
   (4)可以循环从控制台输入名字和年龄,打印门票收费情况,如果名字输入n,则退出程序。
 *
    strcmp//比较字符串是否相等的函数
 *
 */

// 定义游客的结构体
struct Visitor
{
    // 成员
    int age;       // 游客的年龄
    char name[20]; // 存储用户输入进来的名字
    int pay;       // 支付钱数
};

// 声明一个函数
void payMoney(struct Visitor *ptr)
{
    // 结构体指针,操作成员用->
    if (ptr->age > 18)
    {
        ptr->pay = 20;
    }
    else
    {
        ptr->pay = 0;
    }
}

int main()
{
    // 创建一个结构体变量
    struct Visitor visitor;
    // // 开启循环
    while (1)
    {
        printf("请您输入您的大名:");
        scanf("%s", visitor.name);
        // 判断客户的名字是不是N,如果是N跳出循环语句
        // 相等判断:最基本保证左右类型一致 visitor.name == 'n'
        if (strcmp(visitor.name, "n") == 0)
        {
            break;
        }
        printf("请您输入您的芳龄:");
        scanf("%d", &visitor.age);
        // 用户输入年龄与名字

        // 票件的判断单独封装到一个函数内部
        payMoney(&visitor);

        printf("亲爱的%s同志,您需要支付%d元\n", visitor.name, visitor.pay);
    }

    // printf("访问结束!\n");

    // char str[] = "Hello";
    // char str1[] = "Hello";
    // 字符串,本质就是字符数组,字符数组比较内存地址
    // printf("%d\n",str==str1);

    // strcmp
    // 相同为0 不同为-1
    // printf("%d\n", strcmp(str, str1));
    // char str[] = "Hello";
    // char str1[] = "Hello";
    // printf("%d\n", str == str1);
    // C语言判断的时候当中字面量=>常量
    // printf("%d\n", "Hello" == "Hello");

    // int a;
    // scanf("%d", &a);

    //  char * str = "哈哈哈";

    return 0;
}

 总结:

        区分三个概念:结构体、结构体变量、结构体变量的成员。

        结构体是自定义的数据类型,表示的是一种数据类型。结构体变量代表一个具体变量。

int num1;        // int 是数据类型, 而num1是一个具体的int变量
struct Car car1; // Car 是结构体数据类型,而car1是一个Car变量

        Car就像一个“汽车图纸”,生成出来的具体的一辆辆汽车,就类似于一个个的结构体变量。这些结构体变量都含有相同的成员,将结构体变量的成员比作“零件”,同一张图纸生产出来的零件的作用都是一样的。

 三:共用体

        有时需要一种数据结构,不同的场合表示不同的数据类型。比如,如果只用一种数据结构表示学生的“成绩”,这种结构就需要有时是整数(80、90),有时是字符('A'、'B'),有时是浮点数(80.5、60.5)。

        C语言提供了共用体类型(Union 结构),用来自定义可以灵活变更的数据结构。它内部可以包含各种属性,但同一时间只能有一个属性,因为所有属性都保存在同一个内存地址,后面写入的属性会覆盖前面的属性。这样做的最大好处是节省空间。

        “共用体”与“结构体”的定义形式相似,但它们的含义是不同的:

  1. 结构体变量所占内存长度,可以认为是各成员占的内存长度的叠加;每个成员分别占有其自己的内存单元。
  2. 共用体变量所占的内存长度等于最长的成员的长度;几个成员共用一个内存区。

(1)声明共用体 

union 共用体类型名称
{
    数据类型 成员名1;
    数据类型 成员名2;
    …
    数据类型 成员名n;
};
union data
{
    int m;
    float x;
    char c;
};

        上例中,union命令定义了一个包含三个属性的数据类型data。虽然包含三个属性,但是同一时间只能取到一个属性。最后赋值的属性,就是可以取到值的那个属性。

(2)声明共用体变量

#include <stdio.h>

int main()
{
    // 共用体类型
    // 第一种写法:先定义类型 在创建共用体变量
    // 第二种写法:定义完类型以后,立马创建共用体变量
    union data
    {
        // 成员:成员之间用分号隔开
        char a;
        int b;
        double c;
    } bj, hb;
    // 创建共用体变量
    union data cn, tw, sz;
    printf("占用内存中字节数:%zu\n", sizeof(cn));

    // 第三种:共用体名字省略....
    union
    {
        int a;
        short b;
        unsigned int c;
    } tj, wh;
    return 0;
}

(3)​​​​​​​成员的获取和赋值

#include <stdio.h>

int main()
{
    // 定义共用体类型
    union data
    {
        // 三个成员
        char ch;
        int a;
        long c;
    };
    // 声明共用体变量
    union data uni;
    printf("字节数:%zu\n", sizeof(uni));
    // 第一种写法:点语法操作成员
    //  成员赋值
    uni.ch = 'z';
    printf("%c\n", uni.ch);
    uni.a = 100;
    printf("%d\n", uni.a);
    uni.c = 97l;
    printf("%ld\n", uni.c);

    return 0;
}
#include <stdio.h>

int main()
{

    // 共用体类型
    union data
    {
        char a;
        int b;
        long c;
    };
    //{.成员1 = 'z',.成员2 = 100,.成员n='xxx'};
    // union data jch = {.a = 'z', .b = 97, .c = 100L};
    // printf("%ld\n", jch.c);

    // 这种语法是给共用体变量的[首个成员] 进行初始化数据
    // 不能用逗号隔开
    union data china = {'z'};
    printf("%c\n", china.a);

    return 0;
}

(4)练习

        现有一张关于学生信息和教师信息的表格。学生信息包括姓名、编号、性别、职业、 分数,教师的信息包括姓名、编号、性别、职业、教学科目:可以参考下面的表格。

        请利用共用体,只使用一个结构体保存每个人的信息。

Name

Num

Sex

Profession

Score / Course

孙二娘

501

女(f)

学生(s)

89.5

吴用

1011

男(m)

老师(t)

math

顾大嫂

109

女(f)

老师(t)

English

林冲

982

男(m)

学生(s)

95.0

#include <stdio.h>
#define TOTAL 2
// 定义结构体,将来结构体变量存储人员信息
struct People
{
    // 成员姓名
    char name[20];
    // 编号
    int num;
    // 性别 女f  男m
    char sex;
    // 职业 学生s 老师 t
    char profession;
    // 老师 课程  学生 分数
    union
    {
        double score;    // 分数
        char course[20]; // 课程
    } st;
};
int main()
{
    // 结构体类型 数组
    struct People arr[TOTAL];
    // 输入信息
    for (int i = 0; i < TOTAL; i++)
    {
        printf("请您录入信息:");
        // 输入信息
        scanf("%s %d %c %c", arr[i].name, &(arr[i].num), &(arr[i].sex), &(arr[i].profession));
        // s别加上双引号,字符!!!
        // 学生
        if (arr[i].profession == 's')
        {
            printf("亲爱的同学,请您输入您的分数:");
            scanf("%lf", &(arr[i].st.score));
        }
        else
        {
            // 老师
            printf("亲爱的老师,请您输入您教授的课程:");
            scanf("%s", arr[i].st.course);
        }
    }

    // 循环打印输出
    for (int j = 0; j < TOTAL; j++)
    {
        // 判断
        if (arr[j].profession == 's')
        {
            printf("姓名:%s,编号:%d,性别:%c,职业:%c,分数:%lf\n", arr[j].name, arr[j].num, arr[j].sex, arr[j].profession, arr[j].st.score);
        }
        else
        {
            printf("姓名:%s,编号:%d,性别:%c,职业:%c,课程:%s\n", arr[j].name, arr[j].num, arr[j].sex, arr[j].profession, arr[j].st.course);
        }
    }
    return 0;
}

 四:typedef

        C语言允许为一个数据类型起一个新的别名,就像给人起“绰号”一样。

        起别名的目的不是为了提高程序运行效率,而是为了编码方便。例如,有一个结构体的名字是 student,定义一个结构体变量stu1。

struct student stu1;

        struct 看起来就是多余的,但不写又会报错。如果为 struct student起了一个别名 Student,书写起来就简单了。

Student stu1;

        这种写法更加简练,意义也非常明确,不管是在标准头文件中还是以后的编程实践中,都会大量使用这种别名。

(1)typedef 应用场景

        用typedef声明数组类型、指针类型,结构体类型、共用体类型等,使得编程更加方便。

为某个基本类型起别名

        typedef 命令用来为某个类型起别名

typedef 类型名 别名;

        习惯上,常把用typedef声明的类型名的第1个字母用大写表示,以便与系统提供的标准类型标识符相区别。

#include <stdio.h>
// typedef:可以给类型起别名 [别整汉字]
typedef char Zifu;
typedef int Zhengshu; // 相当于给int类型起了一个"绰号"Zhengshu
typedef double Xiaoshu;
//一次起多个别名
typedef float Heizi, Gousheng, Erha;
int main()
{
    Zhengshu a = 100;
    Zifu ch = 'z';
    Xiaoshu b = 3.14;
    printf("%d %c %f\n", a, ch, b);
    
    Gousheng p  = 5.5f;
    printf("%f",p);
    return 0;
}

 为结构体、共用体起别名

        为 struct、union等命令定义的复杂数据结构创建别名,从而便于引用。

#include <stdio.h>
// 声明共用体,结构体类型的时候省略名字
typedef union
{
    int a;
    char b;
    double c;
} Union_data;

// 结构体
typedef struct
{
    char *name; // 姓名
    int age;    // 年龄
    char sex;   // 性别
} People;

int main()
{
    // 没有起别名
    // struct Students a = {"小明", 18, 'm'};
    // 起别名
    // People a = {"小明", 18, 'm'};

    // 没有起别名
    // union data a = {.a = 10};
    Union_data a = {.c = 3.14};
    return 0;
}

为指针起别名

        typedef可以为指针起别名。

为数组类型起别名

        typedef也可以用来为数组类型起别名。

#include <stdio.h>
// 指针类型进行起别名
// int *
typedef int *intPtr;
// 字符指针
typedef char *String;
// 数组起别名
typedef int Array[5];
int main()
{
    // int a = 10;
    // intPtr ptr = &a;
    // printf("%d\n", *ptr);

    // String str = "我爱塞北的大雪啊";
    // printf("%s", str);

    Array arr = {10, 20, 30, 40, 50};

    for (int i = 0; i < 5; i++)
    {
        printf("%d\n", arr[i]);
    }

    return 0;
}

 (2)综合使用演示

#include <stdio.h>

// 给字符指针取别名
typedef char *String;

// 给结构体取别名
typedef struct
{
    int id;
    String name;
    String profile;
} User;  

int main()
{
    // 声明一个结构体变量
    User u;

    // 初始化结构体变量的属性赋值
    u.id = 101;
    u.name = "张三";
    u.profile = "一个纯粹的人,一个高尚的人,一个脱离了低级趣味的人。";

    // 打印结构体变量的属性
    printf("id:%d\n", u.id);
    printf("name:%s\n", u.name);
    printf("profile:%s\n", u.profile);

    return 0;
}

 

本章内容到此结束,下一章为动态内存分配与预处理器

关注我,一起学习嵌入式 

  • 37
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值