C语言进阶 11.结构体
文章目录
- C语言进阶 11.结构体
- 11.1. 枚举
- 11.2. 结构类型
- 11.3. 结构与函数
- 11.4. 结构中的结构
- 11.5. 类型定义
- 11.6. 联合
- 11.7. PAT
- 11-0. 平面向量加法(10)
- 11-1. 通讯录的录入与显示(10)
11.1. 枚举
-
常量符号化:
- 用符号而不是具体的数字表示程序中的数字
- const int red = 0;
- const int yellow = 1;
- const int green = 2;
- 用符号而不是具体的数字表示程序中的数字
-
枚举:
-
用枚举而不是定义独立的const int变量
- enum COLOR { RED, YELLOW, GREEN };//0 1 2
-
枚举是一种用户定义的数据类型, 它用关键字enum以如下语法来声明:
- enum枚举类型名字{ 名字0, …, 名字n };
-
枚举类型名字通常并不真的使用, 要用的是在打括号里的名字, 因为它们就是常量符号, 它们的类型是int, 值则依次从0到n, 如:
- enum colors { red, yellow, green };//三个常量, red是0, yellow是1, green是2
-
当需要一些可以排列起来的常量值时, 定义枚举的意义就是给了这些常量值名字
-
枚举类型可以跟上enum作为类型
- void f(enum COLOR c);
-
它是以整数来做内部计算和外部输入输出的
-
-
套路: 自动计数的枚举:
- 这样需要遍历所有的枚举量或者建立一个用枚举量做下标的数组的时候就很方便了
- enum COLOR { RED, YELLOW, GREEN, NumCOLORS };//NumCOLORS就表示这个枚举有多少个量
char* ColorNames[NumCOLORS] = {"red","yellow","green", };
- 这样需要遍历所有的枚举量或者建立一个用枚举量做下标的数组的时候就很方便了
-
枚举量:
- 声明枚举量的时候可以指定值
- enum COLOR { RED = 1; YELLOW, GREEN = 5 };//1 2 5
- 声明枚举量的时候可以指定值
-
枚举只是int:
- 即使给枚举类型的变量赋不存在的整数值也没有任何的warning或error
- enum COLOR RED = 3;
- 即使给枚举类型的变量赋不存在的整数值也没有任何的warning或error
-
枚举:
-
虽然枚举类型可以当作类型使用, 但是实际上很(bu)少(hao)用
-
如果有意义上排比的名字, 用枚举比const int方便
-
枚举比宏(macro)好, 因为枚举有int类型
-
#include <stdio.h>const int red = 0;
const int yellow = 1;
const int green = 2;enum COLOR { RED, YELLOW, GREEN };//0 1 2void f(enum COLOR c);int main(int argc, char const* argv[]) {int color = -1;scanf("%d", &color);char* colorName = NULL;printf("%d %d %d\n", RED, YELLOW, GREEN);switch (color) {case RED: colorName = "red"; break;case YELLOW: colorName = "yellow"; break;case GREEN: colorName = "green"; break;default: colorName = "unknown"; break;}printf("%s\n", colorName);return 0;
}
#include <stdio.h>enum COLOR { RED, YELLOW, GREEN, NumCOLORS };int main(int argc, char const* argv[]) {int color = -1;char* ColorNames[NumCOLORS] = {"red","yellow","green",};char* colorName = NULL;scanf("%d", &color);if (color >= 0 && color < NumCOLORS) {colorName = ColorNames[color];}else {colorName = "unknown";}printf("%s\n", colorName);return 0;
}
11.2. 结构类型
-
声明结构类型:
- 一个结构就是一个复合的数据类型, 里面可以有各种不同类型的成员, 用一个变量来表达它们
struct date { //一个结构可以有多个成员int month;int day;int year;//成员变量 }; //初学者最常见的错误: 漏了这个分号
- 一个结构就是一个复合的数据类型, 里面可以有各种不同类型的成员, 用一个变量来表达它们
-
在函数内/外?
- 和本地变量一样, 在函数内部声明的结构类型只能在函数内部使用
- 所以通常在函数外部声明结构类型, 这样就可以被多个函数所使用了
-
声明结构的形式:
-
- struct point{
int x;
int y;
};
struct point p1, p2;
- p1和p2都是point里面有x和y的值
- struct point{
-
- struct {
int x;
int y;
} p1, p2;
- p1和p2都是一种无名结构, 里面有x和y
- struct {
-
- struct point {
int x;
int y;
} p1, p2;
- p1和p2都是point里面有x和y的值
- struct point {
-
第一和第三种形式, 都声明了结构point并定义了两个变量, 而第二种形式没有声明point
-
-
结构变量:
-
struct date today; //定义结构变量
-
today.month = 07; //给变量赋值
today.day = 31;
today.year = 2014;
-
-
结构的初始化:
- struct date today = { 07, 31, 2014 };
- struct date today = { .month = 07, .year = 2014 };//.day没有初始化, 和数组初始化一样, 默认为0
-
结构成员:
-
结构和数组有点像, 数组当中有很多的单元, 而结构当中有很多的成员, 不同的是, 数组当中的单元只能是相同类型的, 而结构当中的成员可以是不同类型的
-
数组用[]运算符和下标访问其成员
- a[0] = 10;
-
结构用.运算符和名字访问其成员
today.day
today.month
today.year
-
-
结构运算:
-
要访问整个结构, 直接用结构变量的名字
-
对于整个结构, 可以做赋值, 取地址, 也可以传递给函数参数
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>struct date { //声明结构类型int month;int day;int year;
};
struct point {int x;int y;
};
int main(int argc, char const* argv[]) {struct date today = { 07, 31, 2014 }; //定义结构变量today.month = 07; //访问结构成员today.day = 31;today.year = 2014;today = (struct date){ 07, 31, 2014 };//类型转换struct date day;day = today; //结构变量给结构变量赋值, 而数组是不能这样做的day.year = 2015;struct date* pDate = &today;printf("Today's date is %i-%i-%i.\n", today.year, today.month, today.day);printf("day's date is %i-%i-%i.\n", day.year, day.month, day.day);printf("address of today is %p\n", pDate);return 0;
}
11.3. 结构与函数
-
没听懂
-
结构作为函数参数:
-
int numberOfDays(struct date d)
-
整个结构可以作为参数的值传入函数
-
这时候是在函数内新建一个结构变量, 并复制调用者的结构的值
-
也可以返回一个结构
-
这与数组是完全不同的
-
-
输入结构:
-
没有直接的方式可以一次scanf一个结构
-
如果我们打算写一个函数来读入结构
-
但是读入的结构如何送回来呢?
-
记住C在函数调用时是传值的
-
所以函数中的p与main中的y是不同的
-
在函数读入了p的数值之后, 没有任何东西回到main, 所以y还是{0,0}
-
-
解决方案:
-
之前的方案, 把一个结构传入了函数, 然后再函数中操作, 但是没有返回回去
- 问题在于传入函数的是外面那个结构的克隆体, 而不是指针
- 传入结构和传入数组是不同的
- 问题在于传入函数的是外面那个结构的克隆体, 而不是指针
-
在这个输入函数中, 完全可以创建一个临时的结构变量, 然后把这个结构返回给调用者
-
-
-
结构指针作为参数:
- C语言经典教材<<K & R>>说过(p.131)
- If a large structure is to be passed to a function, it is generally more efficient to pass a pointer than to copy the whole structure.
- 传一个结构给函数的时候, 应该传指针, 而不是整个结构
- C语言经典教材<<K & R>>说过(p.131)
-
指向结构的指针:
-
用->表示指针所指的结构变量中的成员
-
(*p).month = 12;
-
p->month = 12;//这个写法更简洁, ->读作arrow
-
有了这个运算符之后, 就可以将3当中实现的代码修改
-
-
输入今天的日期, 打印明天的日期
#include <stdio.h>
#include <stdbool.h>struct date {int month;int day;int year;
};bool isLeap(struct date d);
int numberOfDays(struct date d);int main(int argc, char const* argv[]) {struct date today, tomorrow;printf("Enter today's date (mm dd yyyy):");scanf("%i %i %i", &today.month, &today.day, &today.year);if (today.day != numberOfDays(today)) {tomorrow.day = today.day + 1;tomorrow.month = today.month;tomorrow.year = today.year;}else if (today.month == 12) {tomorrow.day = 1;tomorrow.month = 1;tomorrow.year = today.year + 1;}else {tomorrow.day = 1;tomorrow.month = today.month + 1;tomorrow.year = today.year;}printf("Tomorrow's date is %i-%i-%i.\n", tomorrow.year, tomorrow.month, tomorrow.day);return 0;
}int numberOfDays(struct date d) {int days;const int daysPerMonth[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };if (d.month == 2 && isLeap(d)) {days = 29;}else {days = daysPerMonth[d.month - 1];}return days;
}bool isLeap(struct date d) {bool leap = false;if ((d.year % 4 == 0 && d.year % 100 != 0) || d.year % 400 == 0) {leap = true;}return leap;
}
- 方案一完成入读结构的功能, 行不通
#include <stdio.h>struct point {int x;int y;
};void getStruct(struct point);
void output(struct point);int main(void) {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\n", p.x, p.y);
}
方案二
#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\n", p.x, p.y);
}
- ->运算符
#include <stdio.h>struct date {int month;int day;int year;
}myday;int main(void) {struct date* p = &myday;(*p).month = 12;p->month = 12;printf("%i\n", (*p).month);printf("%i\n", p->month);return 0;
}
- 修改后
#include <stdio.h>struct point {int x;int y;
};struct point* getStruct(struct point* p);
void output(struct point);
void print(const struct point* p);int main(int argc, char const* argv[]) {struct point y = { 0,0 };getStruct(&y);output(y);output(*getStruct(&y));print(getStruct(&y));*getStruct(&y) = (struct point){ 1,2 };
}struct point* getStruct(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\n", p.x, p.y);
}
void print(const struct point* p) {printf("%d, %d\n", p->x, p->y);
}
11.4. 结构中的结构
- 没听懂
- 结构数组:
struct date dates[100]; struct date dates[] = {{4,5,2005}, {2,4,2005} }
- 结构中的结构:
struct dateAndTime{struct date sdate;struct time stime;};
#include <stdio.h>struct time {int hour;int minutes;int seconds;
};struct time timeUpdate(struct time now);int main(void) {struct time testTimes[5] = {{11,59,59}, {12,0,0},{1,29,59},{23,59,59},{19,12,27}};return 0;
}
struct time timeUpdate(struct time now) {}
11.5. 类型定义
-
自定义数据类型(typedef):
-
C语言提供了一个typedef的功能来声明一个已有的数据类型的新名字, 改善了程序的可读性, 比如:
- typedef int Length;
- 使得Length成为int类型的别名
-
这样, Length这个名字就可以代替int出现在变量定义和参数声明的地方了:
- Length a,b,len;
- Length numbers[10]; //但是这样感觉比原来还复杂, 所以typedef就用在定义结构的别名时使用就可以
-
typedef struct {int month;int day;int year;} mydate; //这里是没有名字的struct, mydate是struct的别名
#include <stdio.h>typedef struct date {int month;int day;int year;
} mydate; //在typedef和最后的单词中间的东西都是原来的, 最后的单词mydate是struct date的别名int main(int argc, char const* argv[]) {typedef int Length;Length a, b, len;Length numbers[10];mydate d = { 9, 1, 2005 };return 0;
}
11.6. 联合
-
联合(union):
union AnEit {int i;char c;} elt1, elt2;
-
这个结构中有两个成员i和c, 对于是struct, i和c的值是分开的, 随时都可以使用其中的 任何一个, 而对于union, i和c是联合的, 它们都占据了相同的内存空间
-
联合: 所有的成员共享一个空间, 同一时间只有一个成员是有效的, union的大小是其最大的成员
-
-
union的用处:
- 通过这个方法可以得到一个int或者double或者其它类型的内部的各个字节
#include <stdio.h>union AnEit {int i;char c;
} elt1, elt2;int main(int argc, char const* argv[]) {elt1.i = 4;elt2.c = 'a';elt2.i = 0xDEADBEEF;return 0;
}
- union的用处:
#include <stdio.h>typedef union {int i;char ch[sizeof(int)];
} CHI;int main(void) {CHI chi;int i;chi.i = 1234; //0x04D2for (i = 0; i < sizeof(int); i++) {printf("%02hhX", chi.ch[i]); //小端:000004D2 大端:D2040000}printf("\n");return 0;
}
11.7. PAT
-
11-0. 平面向量加法(10)
-
疑问:
-
如果处理这个功能: 不能输出-0.0
-
fabs()函数:
- 返回某一个值的绝对值
- #include <math.h>
-
-
本题要求编写程序,计算两个二维平面向量的和向量。
-
输入格式:
输入在一行中按照“x1 y1 x2 y2”的格式给出两个二维平面向量V1=(x1, y1)和V2=(x2, y2)的分量。 -
输出格式:
在一行中按照“(x, y)”的格式输出和向量,坐标输出小数点后1位(注意不能输出-0.0)。 -
输入样例:
3.5 -2.7 -13.9 8.7 -
输出样例:
(-10.4, 6.0)
-
-
看着答案完成的处理-0.0功能
#include <stdio.h>
#include <math.h>//声明结构类型
struct xiangLiang {double x;double y;
} xY, Xy; //定义结构变量int main() {//引用结构成员进行输入scanf("%lf %lf %lf %lf", &xY.x, &xY.y, &Xy.x, &Xy.y);//结果变量double resultX = xY.x + Xy.x;double resultY = xY.y + Xy.y;//判断-0.0的情况if (fabs(resultX) < 0.05) {resultX = fabs(resultX);}if (fabs(resultY) < 0.05) {resultY = fabs(resultY);}printf("(%.1f, %.1f)\n", resultX, resultY);//求和并输出//printf("(%.1f, %.1f)\n", xY.x + Xy.x, xY.y + Xy.y);return 0;
}
- 答案
答案
#include <stdio.h>
#include <math.h>
#define EPSILON 0.05struct Vector {double x;double y;
};int main(void)
{struct Vector v1, v2, v3;scanf("%lf %lf %lf %lf", &v1.x, &v1.y, &v2.x, &v2.y);v3.x = v1.x + v2.x;v3.y = v1.y + v2.y;if(fabs(v3.x) < EPSILON)v3.x = fabs(v3.x);if(fabs(v3.y) < EPSILON)v3.y = fabs(v3.y);printf("(%.1f, %.1f)\n", v3.x, v3.y);return 0;
}
-
11-1. 通讯录的录入与显示(10)
-
疑问:
-
如何查询记录编号?
-
已解决, 通过结构数组的方式将输入的结构成员信息和输入的记录编号存放起来, 遍历数组, 使用下标和.访问数组和结构中的单元和成员, 打印输出
-
-
通讯录中的一条记录包含下述基本信息:朋友的姓名、出生日期、性别、固定电话号码、移动电话号码。本题要求编写程序,录入N条记录,并且根据要求显示任意某条记录。
-
输入格式:
输入在第1行给出正整数N(<=10);随后N行,每行按照格式“姓名 生日 性别 固话 手机”给出一条记录。其中“姓名”是不超过10个字符、不包含空格的非空字符串;生日按“yyyy/mm/dd”的格式给出年月日;性别用“M”表示“男”、“F”表示“女”;“固话”和“手机”均为不超过15位的连续数字,前面有可能出现“+”。
在通讯录记录输入完成后,最后一行给出正整数K,并且随后给出K个整数,表示要查询的记录编号(从0到N-1顺序编号)。数字间以空格分隔。 -
输出格式:
对每一条要查询的记录编号,在一行中按照“姓名 固话 手机 性别 生日”的格式输出该记录。若要查询的记录不存在,则输出“Not Found”。 -
输入样例:
3
Chris 1984/03/10 F +86181779452 13707010007
LaoLao 1967/11/30 F 057187951100 +8618618623333
QiaoLin 1980/01/01 M 84172333 10086
2 1 7 -
输出样例:
LaoLao 057187951100 +8618618623333 F 1967/11/30
Not Found
-
-
看着答案写的
#include <stdio.h>struct tongXunLu {//朋友的姓名、出生日期、性别、固定电话号码、移动电话号码char name[10];struct birthday {int year;int month;int day;} date;char sex;char guHua[15];char phone[15];
};int main(int argc, char const* argv[]) {//定义结构变量struct tongXunLu inf[10];//输入nint n;scanf("%d", &n);//循环输入n次int i;for (i = 0; i < n; i++) {//输入信息scanf("%s %i/%i/%i %c %s %s", &inf[i].name, &inf[i].date.year, &inf[i].date.month, &inf[i].date.day, &inf[i].sex, &inf[i].guHua, &inf[i].phone);}//输入kint k;scanf("%d", &k);//输入k个整数//num[i]表示查询的记录编号int num[10];for (i = 0; i < k; i++) {scanf("%d", &num[i]);}for (i = 0; i < k; i++) {if (num[i] >= 0 && num[i] < n) {//姓名 固话 手机 性别 生日printf("%s %s %s %c %i/%i/%i\n", inf[num[i]].name, inf[num[i]].guHua, inf[num[i]].phone, inf[num[i]].sex, inf[num[i]].date.year, inf[num[i]].date.month, inf[num[i]].date.day);}else {printf("Not Found\n");}}return 0;
}
- 答案
#include <stdio.h>
#define N 10struct Person {char name[11];char birthday[11];char sex;char fixed[17];char mobile[17];
};int main(void){struct Person p[N];int num[N];int i, n, k;scanf("%d", &n);for (i = 0; i < n; ++i){scanf("%s %s %c %s %s", &p[i].name, &p[i].birthday,&p[i].sex, &p[i].fixed, &p[i].mobile);}scanf("%d", &k);for (i = 0; i < k; ++i) {scanf("%d", &num[i]);}for(i = 0; i < k; ++i) {if (num[i] >= 0 && num[i] < n) {printf("%s %s %s %c %s\n", p[num[i]].name, p[num[i]].fixed, p[num[i]].mobile, p[num[i]].sex, p[num[i]].birthday);}else {printf("Not Found\n");}}return 0;
}