目录
– 枚举
– 引入
– 概念
– 语法
– 说明
– 套路
– 枚举量
– 枚举只是int
– 小总结
– 结构
– 声明
– 注意点
– 初始化
– 调用
– 运算
– 结构指针
– 结构与函数
– 结构作为函数的参数和返回值
– 输入结构
– 错误示范
– 结构中的结构
– 结构数组
– 指针
– 自定义类型定义
– 联合
– 语法
– 特点
– 用法
枚举
引入
为了消除MagicWord, 我们会将一些字面量定义成常量
例如:
const char RED="#ff0000";
但是考虑到会有多个同一方面(例如都是颜色)的常量
我们引入枚举这个概念
概念
枚举是一种用户定义的数据类型
使用关键字 enum (全称: enumeration)
语法
声明一个枚举类型
enum 枚举类型名字 {名字0, 名字1, ... 名字n}
这里的”名字”被称为 枚举量
定义一个这种数据类型的变量
enum 枚举类型名字 变量名 = 名字i
可以把这个枚举类型看作和int一样
在其他输入输出方面, 都和int一致
说明
- 枚举类型名字通常不会真的使用, 重要的是大括号中的名字
- 大括号中的名字相当于常量的变量名
- 这些常量的类型必须是int
- 初始值依次从
0
到n
- 枚举的实质就是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的每个成员 都占据同一块空间
- 对其中一个成员赋值, 会覆盖掉其他成员
用法
最常用的场合, 是查看数据在内存中的 储存形式
例如一个联合中有i
和char[sizeof(int)]
这两个成员
当i
中写入一个整数后, 可以通过遍历char
数组查看每个字节的值
这有助于我们理解数据内部存储的情况
同时, 在做文件处理时, 也可以用这种方式将数据转化为二进制处理