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[]; //伸缩型数组成员
};
声明一个 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 * ) //对应的函数指针
声明了函数指针后,可以把类型匹配的函数地址(函数名)赋值给他。