L10_枚举和结构___2022-12-28

目录


枚举

引入

为了消除MagicWord, 我们会将一些字面量定义成常量

例如: const char RED="#ff0000";

但是考虑到会有多个同一方面(例如都是颜色)的常量
我们引入枚举这个概念

概念

枚举是一种用户定义的数据类型
使用关键字 enum (全称: enumeration)

语法

声明一个枚举类型
enum 枚举类型名字 {名字0, 名字1, ... 名字n}

这里的"名字"被称为 枚举量

定义一个这种数据类型的变量
enum 枚举类型名字 变量名 = 名字i

可以把这个枚举类型看作和int一样 在其他输入输出方面, 都和int一致

说明

  • 枚举类型名字通常不会真的使用, 重要的是大括号中的名字
  • 大括号中的名字相当于常量的变量名
  • 这些常量的类型必须是int
  • 初始值依次从0n
  • 枚举的实质就是int, ta的意义就是为了方便

套路

由于声明的时候是从0开始赋值的
此时在最后一个名字的后面再加一个名字(一般是numberofxxx)
那么ta的值就是符号的数量
之后在处理循环的时候会很方便

枚举量

声明枚举量的时候可以指定值
没有被指定, 就接着前面的计数 enum COLOR {RED = 3, YELLOW, GREEN=7};
此时YELLOW的值就是4

枚举只是int

如果给枚举类型的变量赋予 不存在/没有意义的值/其他类型 的值
不会报错

小总结

总而言之, 只有当名字在寓意上连贯有意义的情况
也就是比起使用多次const int方便的时候
才会选择使用枚举

结构

要用一个整体表示
一个人的名字, 生日…
时间的年月日…
要使用结构类型

声明

  • 形式1:
struct 结构类型{
    int 结构成员1;
    char 结构成员2;
    double 结构成员3;
};
struct 结构类型 结构变量1, 结构变量2;
  • 形式2:
struct {
    int 结构成员1;
    char 结构成员2;
    double 结构成员3;
}结构变量1, 结构变量2;

无名类型, 以后用不到

  • 形式3:
struct 结构类型{
    int 结构成员1;
    char 结构成员2;
    double 结构成员3;
}结构变量1, 结构变量2;

注意点

  • 声明结构类型也是一条语句, 最后的分号不能漏
  • 在函数的内部声明结构, 出了函数就用不了, 一般在外部声明
  • 声明结构类型声明结构变量 是两个操作, 在上面的形式1就分开了
  • 单独声明结构变量的时候不能漏了操作符 struct

初始化

struct 结构类型 结构变量1 = {7, 'Z', 6.4};
struct 结构类型 结构变量2 = {.成员类型1=7, .成员类型3=6.4};

在第二种初始化的形式, 成员类型2没有赋予初始值, 会自动赋0

调用

int a = 结构变量1.结构成员1;

.也是一个运算符, 用来访问结构成员
结构类型是一种类型, 对ta使用.没有意义

运算

对于整个结构变量, 可以做 赋值/取地址/作为参数传递 这些操作

结构变量1 = (struct point){5, 'b', 9.7};
结构变量2 = 结构变量1;

结构指针

和数组有所区别, 结构变量的名字不是地址
必须使用&运算符取地址

结构与函数

结构作为函数的参数和返回值

type function_name(struct struct_name para)
这个函数的参数是一个 结构变量

  • 整个结构可以作为参数的值传入函数
  • 其实质是, 在函数内新建一个结构变量,
    复制调用者的结构的值

    函数内发生的不会影响传入的那个结构
    因为函数内部的是新的结构
    注意区别于数组

  • 可以返回结构

输入结构

没有直接的方式可以一次scanf一个结构
可以自己写一个函数实现

错误示范

struct point
{
    int x;
    int y;
};

void inputStruct(struct point p);
void outputStruct(struct point p);

int main()
{
    struct point p0 = {0, 0};
    inputStruct(p0);
    outputStruct(p0);
    return 0;
}

void inputStruct(struct point p)
{
    scanf("%d", &p.x);
    scanf("%d", &p.y);
    printf("在inputStruct中:\n");
    printf("x: %d\ny: %d\n", p.x, p.y);
}
void outputStruct(struct point p)
{
    printf("在outputStruct中:\n");
    printf("x: %d\ny: %d\n", p.x, p.y);
}

输入: 12 23 输出结果:

12 23
在inputStruct中:
x: 12
y: 23
在outputStruct中:
x: 0
y: 0

这里main函数中定义的p0并未改变
这是因为, 传入的参数是复制过去的p, 已经不是p0

结构中的结构

结构数组

struct point points[] = {{x1,x2}, {1,2}, {0,0}};

举例来说
一个矩形可以用左上角右下角两个来确定
每个, 又是由两个坐标组成的
这时候就产生了嵌套的结构, 此时定义一个矩形的方式如下

struct point
{
    int x;
    int y;
};
struct rectangle{
    struct point pt1;
    struct point pt2;
};
struct rectangle r;

这里的矩形 r 有四个值:

  • r.pt1.x
  • r.pt1.y
  • r.pt2.x
  • r.pt2.y

指针

仍旧以矩形为例, 此时如果再定义一个矩形指针*rp:
struct rectangle r,*rp;
那么有四个形式是等价的:

  • r.pt1.x
  • rp->pt1.x
  • (r.pt1).x
  • (rp->pt1).x

注意, 以下这个形式是错误的:

  • rp->pt1->x

因为pt1是结构, 不是指针

自定义类型定义

在定义完一个结构之后, 每次使用都必须在前面加上struct
这给人以一种 “不是亲生” 的感觉…
typedef就允许你"真正"使用自定义的数据类型

typedef用以声明一个已有数据类型的 别名
typedef int Length;
声明后, 两者用法一致

typedef struct ADate{...} Date;
Date d = {...};

在使用结构时, typedef尤其方便 上面的例子中, ADate可以省略不写, 因为最后实际使用的是Date

联合

联合 (union) 在表面上看起来与 结构(struct)是极为相似的

语法

union Test
{
    int i;
    char c;
} union1, union2;

特点

  • union的每个成员 都占据同一块空间
  • 对其中一个成员赋值, 会覆盖掉其他成员

用法

最常用的场合, 是查看数据在内存中的 储存形式
例如一个联合中有ichar[sizeof(int)]这两个成员
i中写入一个整数后, 可以通过遍历char数组查看每个字节的值
这有助于我们理解数据内部存储的情况
同时, 在做文件处理时, 也可以用这种方式将数据转化为二进制处理