第10周:枚举、结构、联合
标签(空格分隔): C
10.1.1 枚举:枚举
常量符号化
- 用符号而不是具体的数字来表示程序中的数字
枚举
- 用枚举而不是定义独立的const int变量
- 枚举是一种用户定义的数据类型,它用关键字 enum 以如下语法来声明:
- enum 枚举类型名字{名字0,……,名字n};
- 枚举类型名字通常并不真的使用,要用的是在大括号里的名字,因为它们就是常量符号,它们的类型是int,值则依次从0到n。如:
- enum colors{red,yellow,green};
- 就创建了三个常量,red的值是0,yellow的值是1,green的值是2。
- 当需要一些可以排列起来的常量值时,定义枚举的意义就是给了这些常量值名字。
- 枚举量可以作为值
- 枚举类型可以跟上enum作为类型
- 但是实际上是以整数来做内部计算和外部输入输出的
套路:自动计数的枚举
- enum colors{red,yellow,green,NumCOLORS};
- 这样需要遍历所有的枚举量或者需要建立一个用枚举量做下标的数组的时候就很方便了
枚举量
- 声明枚举量的时候可以指定值
- enum color{red=1, yellow,green=5};
枚举只是int
- 即使给枚举类型的变量赋不存在的整数值也没有任何warning或error
- 虽然枚举类型可以当做类型使用,但是实际上很(bu)少(hao)用
- 如果有意义上排比的名字,用枚举比const int方便
- 枚举比宏(macro)好,因为枚举有int类型
10.2.1 结构:结构类型
声明结构类型
#include <stdio.h>
int main(int argc, char const *argv[])
{
struct date{
int month;
int day;
int year;
};
struct date today;
today.month = 04;
today.day = 16;
today.year = 2018;
printf("Today's date is %i-%i-%i.\n",
today.year,today.month,today.day);
return 0;
}
Today's date is 2018-4-16.
在函数内/外?
- 和本地变量一样,在函数内部声明的结构类型只能在函数内部使用
- 所以通常在函数外部声明结构类型,这样就可以被多个函数使用了
声明结构的形式
struct point{
int x;
int y;
};
struct point p1,p2;
p1和p2都是point,里面有x和y的值
struct{
int x;
int y;
} p1,p2;
p1和p2都是一种无名结构,里面有x和y
struct point{
int x;
int y;
} p1,p2;
p1和p2都是point,里面有x和y的值
结构变量
- struct date today;
today.month = 04;
today.day = 16;
today.year = 2018;
结构初始化
- struct date today{04,16,2018};
- struct date thismonth{.month=4, .year=2018};
结构成员
- 结构和数组有点像
- 数组用[]运算符和下标访问其成员
- a[0] = 10;
- 结构用 . 运算符和名字访问其成员
- today.day
- student.firstName
- p1.x
- p1.y
结构运算
- 要访问整个结构,直接用结构变量的名字
- 对于整个结构,可以做赋值、取地址,也可以传递给函数参数
- p1 = (struct point){5,10}; //相当于p1.x=5, p1.y=10;
- p1=p2; //相当于p1.x=p2.x; p1.y=p2.y; 数组无法做这两种运算
结构指针
- 和数组不同,结构变量的名字并不是结构变量的地址,必须使用&运算符
- struct date *pDate = &today;
#include <stdio.h>
int main(int argc, char const *argv[])
{
struct date{
int month;
int day;
int year;
};
struct date today;
today=(struct date){04,16,2018};
struct date *pDate = &today;
printf("Today's date is %i-%i-%i.\n",
today.year,today.month,today.day);
printf("address of today is %p\n", pDate);
return 0;
}
10.2.2 结构:结构与函数
结构作为函数参数
- 整个结构可以作为参数的值传入函数
- 这时候是在函数内新建一个结构变量,并复制调用者的结构的值
- 也可以返回一个结构
- 这与数组完全不同
输入结构
- 没有直接的方式可以一次scanf一个结构
- 如果我们打算写一个函数来读入结构
#include <stdio.h>
struct point {
int x;
int y;
};
void getStruct(struct point);
void output(struct point);
int main(int argc, char const *argv[])
{
struct point y = {0,0};
getStruct(y);
output(y);
}
void getStruct(struct point p)
{
scanf("%d", &p.x);
scanf("%d", &p.y);
printf("%d, %d\n", p.x, p.y);
}
void output(struct point p)
{
printf("%d, %d", p.x, p.y);
}
12 23
12,23
0,0
- 但是读入的结构如何送回来呢?
- 记住C在函数调用时是传值的
- 所以函数中的p与main中的y是不同的
- 在函数读入了p的数值之后,没有任何东西回到main,所以y还是{0,0}
解决的方案
- 之前的方案,把一个结构传入了函数,然后在函数中操作,但是没有返回回去
- 问题在于传入函数的是外面那个结构的克隆体,而不是指针
- 传入结构和传入数组是不同的
- 在这个输入函数中,完全可以创建一个临时的结构变量,然后把这个结构返回给调用者
#include <stdio.h>
struct point {
int x;
int y;
};
struct point getStruct(void);
void output(struct point);
int main(int argc, char const *argv[])
{
struct point y = {0,0};
y = getStruct();
output(y);
}
struct point getStruct(void)
{
struct point p;
scanf("%d", &p.x);
scanf("%d", &p.y);
printf("%d, %d\n", p.x, p.y);
return p;
}
void output(struct point p)
{
printf("%d, %d", p.x, p.y);
}
12 23
12, 23
12, 23
结构指针作为参数
- K&R p131
- “如果你有一个大的结构变量需要传入函数,通常更为有效的方式是传指针而不是拷贝整个结构。”
指向结构的指针
struct date {
int month;
int day;
int year;
} myday;
struct date *p = &myday;
(*p).month = 12;
p -> month = 12;
- 用->表示指针所指的结构变量中的成员
10.2.3 结构:结构中的结构
结构数组
- struct date dates[100];
- struct date dates[] = { {4,5,2005},{2,4,2005} };
结构中的结构
struct dateAndTime {
struct date sdate;
struct time stime;
};
嵌套的结构
struct point {
int x;
int y;
};
struct rectangle {
struct point pt1;
struct point pt2;
};
如果有变量
struct rectangle r;
就可以有:
r.pt1.x、 r.pt1.y,
r.pt2.x 和 r.pt2.y
如果有变量定义:
struct rectangle r, *rp;
rp = &r;
那么下面的四种形式是等价的:
r.pt1.x
rp->pt1.x
(r.pt1).x
(rp->pt1).x
但是没有rp->pt1->x (因为pt1不是指针)
结构中的结构的数组
#include <stdio.h>
struct point{
int x;
int y;
};
struct rectangle {
struct point p1;
struct point p2;
};
void printRect(struct rectangle r)
{
printf("<%d, %d> to <%d, %d>\n", r.p1.x, r.p1.y, r.p2.x, r.p2.y);
}
int main(int argc, char const *argv[])
{
int i;
struct rectangle rects[] = {
{{1,2}, {3,4}},
{{5,6}, {7,8}}
};// 2 rectangles
for(i=0;i<2;i++) {
printRect(rects[i]);
}
}
<1, 2> to <3, 4>
<5, 6> to <7, 8>
10.3.1 联合:类型定义
自定义数据类型(typedef)
- C语言提供了一个叫做 typedef 的功能来声明一个已有的数据类型的新名字。比如:
- typedef int Length;
- 使得 Length 成为 int 类型的别名。
- 这样,Length 这个名字就可以代替int出现在变量定义和参数声明的地方了:
- Length a,b,len;
- Length numbers[10];
Typedef
- 声明新的类型的名字
- 新的名字是某种类型的别名
- 改善了程序的可读性
typedef long int64_t; //重载已有的类型名字,新名字的含义更清晰,具有可移植性
typedef struct ADate {
int month;
int day;
int year;
} Date; //简化了复杂的数字
int 64_t i = 1000000000;
Date d = {4,17,2018};
typedef int Length;//Length 就等价于int类型
typedef *char[10] Strings;//Strings 是10个字符串的数组的类型
typedef struct node {
int data;
struct node *next;
} aNode;
或
typedef struct node aNode;//这样用aNode就可以代替struct node
10.3.2 联合:联合
联合
union AnElt {
int i;
char c;
} elt1,elt2;
elt1.i = 4;
elt2.c = 'a';
elt2.i = 0xDEADBEEF;
- 选择:
- 成员是
- 一个 int i 还是
- 一个 char c
sizeof(union …) = sizeof(每个成员)的最大值
- 成员是
- 存储
- 所有的成员共享一个空间
- 同一时间只有一个成员是有效的
- union的大小是其最大的成员
- 初始化
- 对第一个成员做初始化
union的用处
#include <stdio.h>
typedef union {
int i;
char ch[sizeof(int)];
} CHI;
int main(int argc, char const *argv[])
{
CHI chi;
int i;
chi.i = 1234;
for (i=0; i<sizeof(int); i++) {
printf("%02hhX", chi.ch[i]);
}
printf("\n");
return 0;
}
FFD2040000