枚举 Enum
枚举:
关键字:enum
(enumeration)
用法:
enum 枚举类型名 {名字 0, 名字 1 …, 名字 n};
注意:
枚举类型名通常不使用,用的是大括号内的名字,它们就是常量符号,类型是 int
,值依次从 0 到 n
如:
enum color { red, yellow, blue };
注意 :
大括号内每个常量符号以 逗号 间隔
大括号后有 分号
用法示例 :
#include <stdio.h> enum color { red, yellow, blue }; void f (enum color x) { printf ("%d\n" , x); } int main (void ) { enum color t = yellow; scanf ("%d" , &t); f(t); return 0 ; }
enum color
作为变量类型应该写完整
变量 t 虽然是 enum color 类型,但可以当作整型来进行内部计算和外部输入输出。
自动计数的枚举
enum color { red, yellow, blue, NumColors };
原理 :像上面这样,在 枚举 的所有元素后增加一个 Num<内容> 表示 枚举 元素的总数
比如上例:
red 值为 0
blue 值为 2
那么 NumColors 值为 3
NumColors 其实 表示的就是这个枚举的总个数
好处: 定义数组大小 和 遍历数组的时候就很方便
示例:
enum color { red, yellow, blue, NumColors }; int main (void ) { char * ColorNames[NumColors] = { "red" , "yellow" , "blue" , }; char * colorName = NULL ; int color = 0 ; printf ("输入你喜欢的颜色所对应的代码:\n" ); scanf ("%d" , &color); if (color >= 0 && color < NumColors) colorName = ColorNames[color]; else colorName = "Unknowe" ; printf ("%s\n" , colorName); return 0 ; }
枚举量
声明枚举量的时候可以指定值
如:enum COLOR {RED = 1,YELLOW , GREEN = 5 };
例如:
enum COLOR { RED = 1 , YELLOW, GREEN = 5 , NumColors, };int main (void ) { printf ("%d\n%d" , YELLOW, NumColors); return 0 ; }
输出:
枚举是不是 int 类型?
enum color { red = 1 , greem, yellow }; int main (void ) { enum color t = 0 ; printf ("%d\n" , t); return 0 ; }
如果我们让编译器将 int
类型的 0 赋给 enum color
类型的 t
编译器不但不会报错,而且连 warning 都没有
但是我们知道 enum color
其实是一个我们定义出来的新的类型
可能现在你对此理解不深刻,等你学了 C++/Java 知道了 类与对象,你就明白了
因此,我们总结出:
枚举的特点
枚举 的类型很少使用
某种情况下,用 枚举 比用 很多const int
方便
枚举 比 宏 好,因为它有 int 类型
结构 Struct
写在前面 :
想理解结构体,不去多看代码,不去自己写是不可能的。
结构和数组,函数,指针混在一起就导致代码可能很长很乱。这没办法,一定要熬过去,要知道这才是 C语言的精髓 ,也是C++的基础。这里我们用的代码还不是很难。希望大家一定要攻克这里。实在想不通了可以后台私信我或者加入QQ群询问。
我写这篇文章看多了这些代码也有点头晕,为了小伙伴们,我也是拼了。
加油。
认识结构体
声明与使用
struct date { int year; int month; int day; }; int main (void ) { struct date today ; today.year = 2020 ; today.month = 2 ; today.day = 9 ; printf ("%d年 - %d月 - %d日\n" , today.year, today.month, today.day); return 0 ; }
声明的三种方式
新手法
struct point { int x; int y; }; struct point p1 , p2 ;
狠人法
struct { int x; int y; }p1, p2;
常用方法
struct date { int x; int y; }p1, p2;
从 数组 看 结构体
结构体的初始化
struct date { int year; int month; int day; }; int main (void ) { struct date today = { 2020 , 2 , 9 }; struct date tomorrow = { .month = 2 , .day = 9 }; printf ("%d - %d - %d\n" , today.year, today.month, today.day); printf ("%d - %d - %d\n" , tomorrow.year, tomorrow.month, tomorrow.day); return 0 ; }
输出:
可以看到,有两种初始化方法:
当你要初始化 struct 内的所有成员时:
1.struct date today = { 2020, 2, 9 };
当你只想给 struct 内的某几个成员赋值时:
2.struct date tomorrow = { .month = 2, .day = 9 };
没有被赋值的成员被初始化为 0 (类似数组)
结构运算
struct date { int year; int month; int day; }p1, p2; int main (void ) { p1 = (struct date){ 2020 , 2 , 9 }; p2 = (struct date){ 1010 , 1 , 5 }; p2 = p1; printf ("%d - %d - %d\n" , p2.year, p2.month, p2.day); return 0 ; }
输出:
p1 = (struct date){2020, 2, 9}
等价于 p1.year = 2020,p1.month = 2,p1.day = 9;
p1 = p2
等价于 p1.year = p2.year …
数组是无法无法做这两种运算的
指针: 结构体需要取地址
struct date today ;struct date * ptoday = &today;
数组名本身就是地址,所以数组可以不用 & 符号
结构体作为函数参数
明天的日期
写一个程序:输入今天的日期,求明天的日期
看着这个问题简单,实际上稍微有点技巧.
给出下面四个特殊日期,大家可以思考一下:
2020 - 1 -31
2020 - 11 - 31
2020 - 12 - 31
2000 - 2 - 28
目测下面这个代码会被我这种喜欢空行的选手写的将近100行,
放在文中会影响阅读, 辛苦一下大家,**公众号后台回复 [0209] **获取代码
附:我觉得这个代码不难,不会的加Q群问吧,Q群关注我的公众号即可看到
结构指针作为参数
K & R 说过 (p.131)
“if a large structure is to be passed to a function,in is generally more efficent to pass a pointer than to copy the whole structure”
大的结构体指针传参更为高效
指针所指的结构变量的成员访问
struct date { int month; int day; int year; }myday; int main (void ) { struct date * p = &myday; (*p).month = 12 ; p->month = 12 ; return 0 ; }
通常我们用第二种方法,即:
p->month
读作 p 所指的 month (英文读作 arrow 箭头)
来实操一下吧,请听题:
问题描述:
我们 输入整型,浮点型可以直接用
scanf("%d")
或者scanf("%lf")
那么我们可以直接像这样输入一个结构体吗?
答案是我们需要自己写一个函数。
下面程序提供一个思路,可以自己改善/创造你的函数
struct point { int month; int day; int year; }; struct point* getStruct (struct point* p) ;void outputStruct (struct point p) ;void printStruct (const struct point* p) ;int main (void ) { struct point y = { 0 , 0 , 0 }; *getStruct(&y); outputStruct(y); outputStruct(*getStruct(&y)); printStruct(&y); return 0 ; } struct point* getStruct (struct point* p) { printf ("Input a date\n" ); scanf ("%d" , &p->month); scanf ("%d" , &p->day); scanf ("%d" , &p->year); return p; } void outputStruct (struct point p) { printf ("outputStruct:\n" ); printf ("%d - %d - %d\n" , p.month, p.day, p.year); } void printStruct (const struct point* p) { printf ("printStruct:\n" ); printf ("%d - %d - %d\n" , p->month, p->day, p->year); }
结构数组
初始化方法
struct date { int month; int day; int year; }; struct date dates [100]; struct date datess [2] = { {2 , 9 , 2020 }, {2 , 10 , 2020 } };
问题描述:
用一个结构数组记录 5 组时间,请求出这组时间下一秒的时间。
和上面年的问题一样,需要考虑进制问题。请思考一下,尝试自己写出。
struct time { int hour; int minute; int second; }; struct time* timeUpdate (struct time* p) { if (p->second != 59 ) { p->second++; } else if (p->minute != 59 ) { p->second = 0 ; p->minute++; } else if (p->hour != 23 ){ p->second = 0 ; p->minute = 0 ; p->hour++; } else { p->second = 0 ; p->minute = 0 ; p->hour = 0 ; } return p; } int main (void ) { struct time testTimes [5] = { {11 , 59 , 59 }, {12 , 0 , 0 }, {1 , 29 , 59 }, {23 , 59 , 59 }, {19 , 12 , 27 } }; int i = 0 ; for (i = 0 ; i < 5 ; i++) { printf ("Now the time is:%d - %d - %d\n" , testTimes[i].hour, testTimes[i].minute, testTimes[i].second); testTimes[i] = *timeUpdate(&testTimes[i]); printf ("one second letter:%d - %d - %d\n" , testTimes[i].hour, testTimes[i].minute, testTimes[i].second); } return 0 ; }
嵌套的结构
嵌套的结构引入
struct point { int x; int y; }; struct rectangle { struct point p1 ; struct point p2 ; }; int main (void ) { struct rectangle r ; r.p1.x; r.p2.y; return 0 ; }
嵌套的结构的成员访问
struct point { int x; int y; }; struct rectangle { struct point p1 ; struct point p2 ; struct point * p3 ; }; int main (void ) { struct rectangle r , *pr ; r.p1.x; (r.p1).x; pr->p1.x; (pr->p1).x; pr->p3->x; return 0 ; }
结构中有结构的数组
struct point { int x; int y; }; struct rectangle { struct point p1 ; struct point p2 ; }; void printStruct (struct rectangle* p, int len) { int i = 0 ; for (i = 0 ; i < len; i++) { printf ("(%d, %d) (%d, %d)\n" , p[i].p1.x, p[i].p1.y, p[i].p2.x, p[i].p2.y); } } int main (void ) { struct rectangle rects [] = { {{2 , 2 }, {4 , 4 } }, {{5 , 6 }, {7 , 8 } } }; int len = sizeof (rects) / sizeof (rects[0 ]); printStruct(rects, len); return 0 ; }
测试一下:
1.有下列代码段,则输出结果为:
struct { int x, y; } s[2 ] = { {1 , 3 }, {2 , 7 } }; printf ("%d\n" ,s[0 ].y / s[1 ].x);
A: 0
B: 1
C: 2
D: 3
2.有如下变量定义,则对data中的a的正确引用是:
struct sk { int a; float b; }data , *p = & data;
A: (*p).data.a;
B: (*p).a;
C: p->data.a;
D: p.data.a;
3.以下两行代码是否出现在一起?
struct { int x; int y; }x;struct { int x; int y; }y;
A: √
B: ×
公众号回复[0209 2]获取答案。
关于结构体的内存对齐等问题我们以后再说
联合
typedef 关键字
自定义类型
关键字 :typedef
例如:
typedef long int64_t ;typedef struct Adate { int month; int day; int year; }date; int main (void ) { int64_t i = (int64_t )1e+10 ; date d = { 2 , 10 , 2020 }; return 0 ; }
请看下面这两段代码,你能分别出他们的意思是什么吗?
struct { int month; int day; int year; }Date;
typedef struct { int month; int day; int year; }Date;
第一个表示:一个没有名字的结构体类型 它有一个结构体变量 Date
第二个表示:将这个结构体类型重定义为 Date
如果你用你的编译器敲一下这段代码,会发现这两段代码的 Date 的颜色是不一样的
认识 联合
特点:
所有成员共享一个空间
同一时间只有一个成员是有效的
union的大小是其最大的成员
怎么去理解?请看下面程序,以32位机器为例
union AnEit { int i; char c; }elt1, elt2; int main (void ) { elt1.i = 4 ; elt2.c = 'a' ; elt2.i = 0xDEADBEEF ; return 0 ; }
union 的常用场景
先看一下这段程序:
typedef union { int i; char ch[sizeof (int )]; }CHI; int main (void ) { CHI chi; int i; chi.i = 1234 ; for (i = 0 ; i < sizeof (int ); i++) { printf ("%02hhx" , chi.ch[i]); } printf ("\n" ); return 0 ; }
它的输出是:
这说明了chi的内存存储情况:
我们用计算机看一下 16 进制的 1234 是什么样子:
chi的大小是 4个字节,我们可以表示 chi.i 为
00 00 04 D2
这种 高位(d2) 放在 低地址; 低位 (00)放在 高地址 的存储模式叫做 小端
我们现在用的 x86 的机器 基本都是这种存储方式。
我们可以利用 union 得到一个 int 或者 double 等等的内部的字节
这是一个有用的工具。当我们讲到文件时候,大家就对它的功能有更熟悉的理解了。