14章 结构和其他数据形式
14.1 示例问题:创建图书目录
一本书有多种信息,书名,作者,页数,价格等等,这些信息有的是字符串,有的是浮点数。
需要一种既有字符串,又有浮点数的数据形式————C结构。
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<string.h> char * s_gets(char * st, int n); #define MAXTITL 41 #define MAXAUTL 31 struct book { // 结构模板,标记是book char title[MAXTITL]; char author[MAXAUTL]; float value; }; int main(void) { struct book library; //声明struct book 类型变量 library printf("Please enter the book title.\n"); s_gets(library.title, MAXTITL); printf("Now enter the author.\n"); s_gets(library.author, MAXAUTL); printf("Now enter the value.\n"); scanf("%f", &library.value); printf("%s by %s: $%.2f\n", library.title, library.author, library.value); printf("%s: \"%s\"($%.2f)\n", library.author, library.title, library.value); printf("Done.\n"); return 0; } char * s_gets(char * st, int n) { char * ret_val; char * find; ret_val = fgets(st, n, stdin); if (ret_val) { find = strchr(st, '\n');//查找换行符 if (find) *find = '\0'; //将换行符换成'\0' else while (getchar() != '\n') //处理输入行剩余的字符 continue; } return ret_val; }
14.2 建立结构声明
结构声明(structure declaration)描述一个结构的组织布局,声明类似:
struct book {
char title[MAXTITL];
char author[MAXAUTL];
float value;
};
14.3 定义结构变量
程序中关键结构变量:
struct book library;
编译器在这里创建一个结构变量library,为其分配内存空间。
14.3.1 初始化结构
和初始化数组类似:
struct book library = { "The ...", "Renee Vivotte", 1.95};
14.3.2 访问结构成员
使用结构成员运算符 . 来访问结构中的成员。如:用library.value访问library的value
14.3.3 结构的初始化器
C99和C11为结构提供了指定初始化器(designated initializer),语法和数组的指定初始化器类似。
struct book surprise = { .value = 10.99};
14.4 结构数组
同样是那个图书问题,一本书用结构可以解决。
如果我们有很多本图书,就可以用结构数组了。
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<string.h> char * s_gets(char *st, int n); #define MAXTITL 40 #define MAXAUTL 40 #define MAXBOOKS 100 #define _CRT_SECURE_NO_WARNINGS struct book { // 结构模板,标记是book char title[MAXTITL]; char author[MAXAUTL]; float value; }; int main(void) { struct book library[MAXBOOKS]; //声明struct book 类型变量 library int count = 0; int index; printf("Please enter the book title.\n"); printf("Enter at the start of a line to stop.\n"); while (count < MAXBOOKS && s_gets(library[count].title, MAXTITL) != NULL && library[count].title[0] != '\0') { printf("Now enter the anthor.\n"); s_gets(library[count].author, MAXAUTL); printf("Now enter the value.\n"); scanf("%f", &library[count++].value); while (getchar() != '\n') continue; if (count < MAXBOOKS) printf("Enter the next title.\n"); } if (count >0) { printf("Here is the list of your books:\n"); for (index = 0; index < count; index++) printf("%s by %s: $%.2f\n", library[index].title, library[index].author, library[index].value); } else printf("No books?\n"); return 0; } char * s_gets(char * st, int n) { char * ret_val; char * find; ret_val = fgets(st, n, stdin); if (ret_val) { find = strchr(st, '\n');//查找换行符 if (find) *find = '\0'; //将换行符换成'\0' else while (getchar() != '\n') //处理输入行剩余的字符 continue; } return ret_val; }
14.4.1 声明结构数组
和其他类型的数组类似
int a[100];
struct book library[MAXBOOK]; //这里只是把int 类型换成struct book类型。
14.4.2 标识结构数组的成员
在结构名后面使用点.运算符
library[0].balue
14.4.3 程序讨论
14.5 嵌套结构
结构内包含另一个结构。
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #define LEN 20 const char * msgs[5] = { " Thank you for the wonderful evening, ", "You certainly prove that a ", "is a special kind of guy. We must get together", "over a delicious ", " and have a few laughs" }; struct names { char first[LEN]; char last[LEN]; }; struct guy { struct names handle; //嵌套结构,结构里包含另一个结构 char favfood[LEN]; char job[LEN]; float income; }; int main(void) { struct guy fellow = { {"Ewen", "Villard"}, "grilled salmon", "personality coach", 68112.00 }; printf("Dear %s,\n\n", fellow.handle.first); //使用嵌套结构,先使用.得到name,再.得到first printf("%s%s.\n", msgs[0], fellow.handle.first); printf("%s%s\n", msgs[1], fellow.job); printf("%s\n", msgs[2]); printf("%s%s%s", msgs[3], fellow.favfood, msgs[4]); if (fellow.income > 150000.0) puts("!!"); else if (fellow.income > 75000.0) puts("!"); else puts("."); printf("\n%40s%s\n", " ", "See you soon,"); printf("%40s%s\n", " ", "Shalala"); return 0; }
14.6 指向结构的指针
/*friends.c --使用指向结构的指针*/ #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #define LEN 20 struct names { char first[LEN]; char last[LEN]; }; struct guy { struct names handle; char favfood[LEN]; char job[LEN]; float income; }; int main(void) { struct guy fellow[2] ={ {{"Ewen", "Villard"}, "grilled salmon", "personality coach", 68112.00 }, {{"Rodney","Swillbelly"}, "tripe", "tabloid editor", 432400.00 } }; struct guy * him; //指向结构的指针 printf("address #1:%p #2: %p\n",&fellow[0],&fellow[1]); him = &fellow[0]; printf("pointer#1:%p #2: %p\n", him, him + 1); printf("him->income is $%.2f: (*him).income is $%.2f\n", him->income, (*him).income); him++; printf("him->favfood is %s: him->handler.last is %s\n", him->favfood, him->handle.last); return 0; }
14.6.1 声明和初始化结构指针
声明结构指针://和其他指针类似
struct guy * him;
barney是一个guy类型的结构:可以这样给指针赋值:
him = &barney;
14.6.2 用指针访问成员
方法1:指针->成员 如:him->income
方法2:(*指针).成员 如:(*him).income
14.7 向函数传递结构的信息
14.7.1 传递结构成员
传递结构成员给函数和传递普通变量给函数并没有什么不同。
#include<stdio.h> #define FUNDLEN 50 struct funds { char bank[FUNDLEN]; double bankfund; char save[FUNDLEN]; double savefund; }; double sum(double, double); int main(void) { struct funds stan = { "Garlic--Melon Bank", 4032.27, "Lucky's Savings and Loan", 8543.94 }; printf("Stan has a total of $%.2f.\n",sum(stan.bankfund,stan.savefund)); // return 0; } double sum(double x, double y) { return (x + y); }
14.7.2 传递结构的地址 (结构指针做参数)
#include<stdio.h> #define FUNDLEN 50 struct funds { char bank[FUNDLEN]; double bankfund; char save[FUNDLEN]; double savefund; }; double sum(const struct funds *); // int main(void) { struct funds stan = { "Garlic--Melon Bank", 4032.27, "Lucky's Savings and Loan", 8543.94 }; printf("Stan has a total of $%.2f.\n", sum(&stan)); // return 0; } double sum(const struct funds * money) // { return (money->bankfund + money->savefund); }
14.7.3 传递结构
传递结构是传值,函数将创建一个结构副本,然后把实际参数的值给这个副本,函数中的操作都是对副本进行的。
#include<stdio.h> #define FUNDLEN 50 struct funds { char bank[FUNDLEN]; double bankfund; char save[FUNDLEN]; double savefund; }; double sum( struct funds moolah); int main(void) { struct funds stan = { "Garlic--Melon Bank", 4032.27, "Lucky's Savings and Loan", 8543.94 }; printf("Stan has a total of $%.2f.\n", sum(stan)); return 0; } double sum( struct funds moolah) //函数创建一个副本moolah,然后对副本进行操作(不会改变实际参数stan) { return( moolah.bankfund + moolah.savefund); }
14.7.4 其他结构特性
一个结构可以赋值给另一个结构(数组不行)。
神奇的是,如果结构的成员是数组,结构间的赋值仍然可以。
14.7.5 结构和结构指针的选择
通常用结构指针作为函数的参数,这样效率较高。如需要防止原始数据被修改,使用const限定符。
14.7.6 结构中的字符数组和字符指针
字符数组比较简单。
字符指针由于只是给出一个地址,但并未分配内存,可能会存到意外的地方。
14.7.7 结构、指针和malloc()
使用malloc()分配内存并使用指针存储该地址。
14.7.8 复合字面量和结构(C99)
14.7.9 伸缩型数组成员(C99)
1.伸缩型数组成员必须是结构的最后一个成员
2.结构必须至少有一个成员
3.伸缩数组的声明类似于普通数组,只是它的方括号内是空的。
例子:
struct flex
{
int count;
double average;
double scores[]; //伸缩型数组成员
double scores[]; //伸缩型数组成员
};
声明一个 struct flex类型的变量后不能使用scores,因为还没给它分配存储空间。
通常使用方法是声明一个struct flex类型的指针,然后用malloc()给它分配存储空间。如:
struct flex *pf;
pf = malloc(sizeof(struct flex) + 5* sizeof(double) );
14.7.10 匿名结构(C11)
C11中,可以用嵌套的匿名结构进行简化。//但是嵌套结构用处不大
struct person
{
int id;
struct { char first[20]; char last[20];}; //匿名结构
}
访问first时,只需将其看出是person的成员
person ted ; ...
ted.first;
14.7.11 使用结构数组的函数
//把结构数组传递给函数 #include<stdio.h> #define FUNDLEN 50 #define N 2 struct funds { char bank[FUNDLEN]; double bankfund; char save[FUNDLEN]; double savefund; }; double sum(const struct funds mooey[], int n); //传入结构数组的函数 int main(void) { struct funds jones[N] = { { "Garlic--Melon Bank", 4032.27, "Lucky's Savings and Loan", 8543.94 }, { "Honest Jack's Bank", 3620.88, "Party Time Savings", 3802.91 } }; printf("The joneses have a total of $%.2f.\n", sum(jones,N) ); *//使用sum函数 return 0; } double sum(const struct funds money[],int n) { double total; int i; for ( i = 0, total = 0; i < n; i++) total += money[i].bankfund + money[i].savefund; return total; }
14.8 把结构内容保存到文件中
("a+b")模式打开文件,a+可以在文件末尾添加,b 使用二进制文件格式,
使用fread()和fwrite()进行读写。
//在文件中保存结构中的内容 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<string.h> #define MAXTITL 40 #define MAXAULT 40 #define MAXBKS 10 char * s_gets(char * st, int n); struct book { char title[MAXTITL]; char author[MAXAULT]; float value; }; int main(void) { struct book library[MAXBKS]; int count = 0; int index, filecount; FILE * pbooks; int size = sizeof(struct book); if ((pbooks = fopen("book.dat", "a+b")) == NULL) { fputs("Can't open book.dat file\n", stderr); exit(1); } rewind(pbooks); while (count<MAXBKS && fread(&library[count],size,1,pbooks) == 1) { if (count == 0) puts("Current contents of book.dat:"); printf("%s by %s $%.2f\n", library[count].title, library[count].author, library[count].value); count++; } filecount = count; if (count == MAXBKS) { fputs("The book.dat file is full.", stderr); exit(2); } puts("Please add new book titles."); puts("Press [enter] at the start of a line to stop."); while (count < MAXBKS && s_gets(library[count].title, MAXTITL) != NULL && library[count].title[0] != '\0') { puts("Now enter the author."); s_gets(library[count].author, MAXAULT); puts("Now enter the value."); scanf("%f", &library[count++].value); while (getchar() != '\n') continue; if (count < MAXBKS) puts("Enter the next title."); } if (count > 0) { puts("Here is the list of your books:"); for (index = 0; index < count; index++) printf("%s by %s:$%.2f\n", library[index].title, library[index].author, library[index].value); fwrite(&library[filecount], size, count - filecount, pbooks); } else puts("No books.\n"); puts("Bye.\n"); return 0; } char * s_gets(char * st, int n) { char * ret_val; char * find; ret_val = fgets(st, n, stdin); if (ret_val) { find = strchr(st, '\n');//查找换行符 if (find) *find = '\0'; //将换行符换成'\0' else while (getchar() != '\n') //处理输入行剩余的字符 continue; } return ret_val; }
14.9 链式结构
10.10 联合简介
联合(union)是一种数据类型,能在同一个内存空间存储不同的数据类型。
定义联合:
union hold {
int digit;
double bigfl;
char letter;
}
hold的联合体可以存储一个int 或一个double或一个char,同一时刻只能三个中的一个。
14.11 枚举类型
(enumerated type)
可以声明符号名称代替整型常量
enum spectrum {red, orange, yellow, green, blue, violet};
14.12 typedef简介(别名)
typedef char * STRING
STRING name , sign ;// 等价char * name , * sign;
14.13 其他复杂的声明
14.14 函数和指针
函数也有地址,指向函数的指针中存储着函数代码的起始位置。
函数指针需要指明函数的返回类型和参数类型:
void ToUpper(char *) //函数
void (*pf) (char * ) //对应的函数指针
声明了函数指针后,可以把类型匹配的函数地址(函数名)赋值给他。