typedef 和 enum
前两课每次用结构体都要写 struct student,有点啰嗦。typedef 能给一个类型起个新名字,写起来短一些:
1#include <stdio.h>2 3typedef struct {4 char name[20];5 int score;6} Student;7 8int main(void) {9 Student s = { "小林", 95 };10 Student t = { "小周", 88 };11 printf("%s:%d\n", s.name, s.score);12 printf("%s:%d\n", t.name, t.score);13 return 0;14}
小林:95 小周:88
Student 就是这个结构体的名字,前面不用再写 struct。这一课讲 typedef,还有另一样常和它一起用的东西:enum。
typedef 起别名
typedef 的写法是:typedef 原来的类型 新名字;。它不造出新的类型,只是给原来的类型多起一个名字,两个名字用起来完全一样:
1#include <stdio.h>2 3typedef int Minutes;4 5Minutes total(Minutes a, Minutes b) {6 return a + b;7}8 9int main(void) {10 Minutes run = 25;11 Minutes walk = 40;12 printf("一共 %d 分钟\n", total(run, walk));13 return 0;14}
一共 65 分钟
Minutes 就是 int,显示时照样用 %d。好处是读代码的人一看就知道这个数是什么:是分钟,不是随便一个整数。你早就用过一个这样的名字:strlen 返回的 size_t,就是标准库用 typedef 起的名字,背后是一种不带负数的整数。
typedef struct
最常见的用法是给结构体起名字。开头的例子把定义和起名写在了一起;拆开写是这样,意思一样:
1#include <stdio.h>2 3struct point {4 int x;5 int y;6};7 8typedef struct point Point;9 10int main(void) {11 Point a = { 3, 4 };12 struct point b = { 1, 2 };13 printf("(%d, %d) 和 (%d, %d)\n", a.x, a.y, b.x, b.y);14 return 0;15}
(3, 4) 和 (1, 2)
Point 和 struct point 是同一个类型,两种写法可以混着用。所以也常见这样一次写完,两个名字都有:
1typedef struct point {2 int x;3 int y;4} Point;
花括号前面的 point 可以不写(开头的例子就没写)。什么时候必须写?结构体里要用到它自己的时候,比如链表的节点要指向下一个节点。下一课就会用到。
enum:有名字的整数
一个订单的状态只有几种:待付款、已付款、已发货、已送达。可以用 0、1、2、3 表示,可过几天再看代码,status == 2 是什么意思就忘了。枚举(enum)给这几个数各起一个名字:
1#include <stdio.h>2 3enum status { PENDING, PAID, SHIPPED, DELIVERED };4 5int main(void) {6 enum status s = SHIPPED;7 printf("PENDING = %d\n", PENDING);8 printf("DELIVERED = %d\n", DELIVERED);9 printf("s = %d\n", s);10 if (s == SHIPPED) {11 printf("已经发货了\n");12 }13 return 0;14}
PENDING = 0 DELIVERED = 3 s = 2 已经发货了
不写数的话,第一个名字是 0,后面一个比一个大 1。这些名字其实就是 int 常量:能比较、能显示成 %d。习惯上全用大写字母,一看就知道是常量。
自己定数
也可以给名字指定数。指定了的就用指定的,后面没指定的接着加 1。比如让星期一是 1:
1#include <stdio.h>2 3enum weekday { MON = 1, TUE, WED, THU, FRI, SAT, SUN };4 5int main(void) {6 enum weekday today = WED;7 printf("今天是一周的第 %d 天\n", today);8 printf("离周日还有 %d 天\n", SUN - today);9 return 0;10}
今天是一周的第 3 天 离周日还有 4 天
这段程序显示什么?
1#include <stdio.h>2 3enum level { LOW = 1, MIDDLE, HIGH = 10, TOP };4 5int main(void) {6 printf("%d %d\n", MIDDLE, TOP);7 return 0;8}
LOW 指定了 1,MIDDLE 没指定,接着加 1 就是 2。HIGH 指定了 10,TOP 接着它加 1,是 11。
switch 配 enum
enum 和 switch 是天生一对:每种情况一个 case,名字比数清楚多了。下面的函数把状态变成一段文字:
1#include <stdio.h>2 3typedef enum { PENDING, PAID, SHIPPED, DELIVERED } Status;4 5void show_status(Status s) {6 switch (s) {7 case PENDING:8 printf("待付款\n");9 break;10 case PAID:11 printf("已付款\n");12 break;13 case SHIPPED:14 printf("已发货\n");15 break;16 case DELIVERED:17 printf("已送达\n");18 break;19 }20}21 22int main(void) {23 show_status(PAID);24 show_status(DELIVERED);25 return 0;26}
已付款 已送达
typedef enum { ... } Status; 和 typedef struct 一样,给枚举起了个短名字,之后写 Status 就行。别忘了每个 case 后面的 break(第 5 课)。
放在一起用
结构体里可以有一个枚举成员。一组订单,每张有编号和状态,数一数还有几张没送到:
1#include <stdio.h>2 3typedef enum { PENDING, PAID, SHIPPED, DELIVERED } Status;4 5typedef struct {6 int id;7 Status status;8} Order;9 10int main(void) {11 Order orders[4] = { { 1001, DELIVERED }, { 1002, SHIPPED }, { 1003, PAID }, { 1004, DELIVERED } };12 int waiting = 0;13 for (int i = 0; i < 4; i++) {14 if (orders[i].status != DELIVERED) {15 printf("订单 %d 还没送到\n", orders[i].id);16 waiting++;17 }18 }19 printf("一共 %d 张\n", waiting);20 return 0;21}
订单 1002 还没送到 订单 1003 还没送到 一共 2 张
点单步看看:每个 Order 是一个框,status 那一格里放的就是一个整数。
小技巧: C 不会检查枚举变量里放的是不是列出来的那几个数:Status s = 7; 照样能编译。所以 switch 里最好加一个 default,碰到意外的数也有个交代。
练一练
写好代码,点检查。提示随便看,实在想不出来再看答案。
长方形
✓用 typedef struct 定义 Rect,成员是 width 和 height(都是 int)。写函数 area,参数是一个 Rect,返回面积。程序要显示 面积:24。
面积:24
1#include <stdio.h>2 3// 在这里定义 Rect 和 area4 5int main(void) {6 Rect r = { 4, 6 };7 printf("面积:%d\n", area(r));8 return 0;9}
杯子的大小
✓写函数 price,参数是一个 Size,用 switch 返回价格:SMALL 是 12,MEDIUM 是 15,LARGE 是 18。程序要显示 12 15 18。
12 15 18
1#include <stdio.h>2 3typedef enum { SMALL, MEDIUM, LARGE } Size;4 5int price(Size s) {6 7}8 9int main(void) {10 printf("%d %d %d\n", price(SMALL), price(MEDIUM), price(LARGE));11 return 0;12}
向右转
✓方向按顺时针排:北、东、南、西。写函数 turn_right,返回向右转之后的方向:北变东,……,西又变回北。程序从北开始转 5 次,每次显示新的方向,最后一行是 东。
东 南 西 北 东
1#include <stdio.h>2 3typedef enum { NORTH, EAST, SOUTH, WEST } Direction;4 5Direction turn_right(Direction d) {6 7}8 9int main(void) {10 char names[4][8] = { "北", "东", "南", "西" };11 Direction d = NORTH;12 for (int i = 0; i < 5; i++) {13 d = turn_right(d);14 printf("%s\n", names[d]);15 }16 return 0;17}
这一课学会了
typedef 原来的类型 新名字;给类型起别名,不造新类型;size_t就是这样来的。typedef struct { ... } Student;之后写Student就行,不用再写struct;结构体要用到自己时,花括号前面要写名字。enum给一组整数起名字:默认从0开始,一个比一个大 1;也可以自己指定,后面的接着加 1。- 枚举的名字就是
int常量,能比较、能用%d显示,习惯上全大写。 switch配enum,每种情况一个case;C 不检查枚举里放的数,最好加default。