C语言学习(十)结构和其他数据形式

参考书:《C Primer Plus》第六版


1. 建立结构声明

如下

struct book{
	char title[MAXTITL];
	char author[MAXTITL];
	float value;
};

后面通过如下声明来使用这个结构:

struct book library;

2. 定义结构变量

可以用类似于初始化数组的方式来初始化一个结构变量:

struct book library={
	"The Pious Pirate and the Devious Damsel",
	"Renee Vivotte",
	1.95
}

用花括号括起来,中间用逗号隔开。
可以用结构成员运算符.来访问结构中的成员。
也可以用指定初始化器初始化结构的部分成员:

struct book suprise={.value=10.99};

3. 结构数组

4. 嵌套结构

一个结构中可以包含另一个结构。

5. 指向结构的指针

指向结构的指针的重要性体现在:1.指向结构的指针比结构本身更易操控;2.在函数中传递结构的指针比传递结构更有效率;3.一个结构中可以包含其他结构的指针。(典型的如链表的结点)
用指针访问成员时常用的是->运算符:library->value
也可以用解引用运算符:*(library).value

6. 向函数传递结构的信息

复合字面量

有时我们只需要一个临时的结构值,我们可以用复合字面量:

(struct book){"The Idiot","Fyodr Dostoyevvsky",6.99};

伸缩型数组成员

C99新增了一个特性:伸缩型数组成员。
首先做如下声明:

struct flex{
	int count;
	double average;
	double scores[];//伸缩型数组成员
};

然后定义这个类型的指针,用malloc()来分配空间。如

struct flex *pf;
pf=malloc(sizeof(struct flex)+5*sizeof(double));

这样我们的指针指向的类型中就包含了大小为5的double数组。

匿名结构

如一个结构中可以包含一个匿名结构:

struct person{
	int id;
	struct {char first[20];char last[20];};//匿名结构
}

7. 将结构内容保存在文件中

加入我们用pbook标识一个文件流,我们可以用fprintf()函数将一个struct book类型的结构变量储存到文件中,但这个方法很没效率:

fprintf(pbook,"%s %s %.2f\n",primer.title,primer.author,primer.value);

对于一些有很多成员的结构,这个方法用起来就很不方便,而且在检索时也存在问题。
更好的方案是用fread()fwrite()读写结构大小的单元。如:

fwrite(&primer,sizeof(struct book),1,pbook);

程序清单1

#include"stdio.h"
#include"stdlib.h"
#include"string.h"
struct book{
    char title[30];
    char author[30];
    float value;
};
int main(void){
    struct book libr[10];
    int count=0;
    FILE *fp;
    if((fp=fopen("book.dat","a+b"))==NULL){
        fputs("Can't open book.dat file",stderr);
        exit(EXIT_FAILURE);
    }
    rewind(fp);
    int i;
    //写入数据,先运行一次,写入数据
    /*
    for(i=0;i<10;i++){
        puts("Enter the book's title, author and value :");
        if(scanf("%s %s %f",libr[i].title,libr[i].author,&libr[i].value)!=3)exit(EXIT_FAILURE);
    }
    for(i=0;i<10;i++){
        fwrite(libr+i,sizeof(struct book),1,fp);
    }
    */
    rewind(fp);
    struct book temp;
    for(i=0;i<10;i++){
        fread(&temp,sizeof(struct book),1,fp);
        printf("title : %s, author: %s ,value :%f\n",temp.title,temp.author,temp.value);
    }
    fclose(fp);
    return 0;
}

fread()fwrite()这两个函数确实非常适合用来读写结构数据。用它来读写结构数据也不容易出错,用法也简单。

8. 链式结构

结构的一个重要用途就是创建新的数据形式。有很多的一些数据结构可以更有效的解决一些特定的问题,数据结构如:队列、二叉树、堆、哈希表、图标。很多数据结构都是链式结构,即每个结构都包含一两个数据项和一两个指向同类型结构的指针。

9 联合简介

联合是一种数据类型,它能在一个内存空间中存储不同的数据类型。它的典型用途是用来设计一种无规律的混合类型的表。联合类型的数组,其中的元素可以是各种类型。
如下是一个带标记的联合模板:

union hold{
	int digit;
	double bigfl;
	char letter;
};

以上声明的结构可以存储一个int或者一个double或者一个char。注意只能存储其中的一个数据,不能同时存储这三个数据。
初始化时可以初始化第一个元素,或者用指定初始化器,或者用另一个联合对象初始化。

union hold valA;
valA.letter='p';
union hold valB=valA;
union hold valC={88};
union hold valD={.bigfl=18.2};

另外也可以用匿名联合,类似于匿名结构。

10. 枚举类型

可以用枚举类型声明符号类型来表示整型常量。用关键字enum,enum常量是int类型的。枚举类型的用途是在某些时候提高程序的可读性。

enum spectrum {red, orange, yellow, green, blue, violet};
enum spectrum color;

实际上redorange都是int类型的常量,red=0,后面的都逐个递增。
也可以为枚举常量指定整数值。

enum levels{low=100, medium=500, high= 2000};

11. typedef 简介

typedef用于为某一特定类型定义别名。用法很简单。

typedef unsigned char BYTE;

可以用typedef来为结构命名,此时可以省略结构标签:

typedef struct {
	float imag;
	float real;
} COMPLEX;

12. 函数和指针

C允许声明一个指向函数的指针。它的一个用途是可以将函数指针作为另一个函数的参数,可以传递特定的函数给一个函数,这在某些场景下是有用的,如在一个数组排序函数中,通过传递一个特定的比较大小的函数可以告诉排序函数依照什么次序给数组排序。在C++中用sort()给某个容器对象排序时就需要传递一个比较大小的函数。C中有qsort()函数,它可以给任意类型的数组排序,但需要传递函数比较元素。
首先,我们知道指针实际上就是地址,某个类型的指针表示某个类型的地址,而函数也有地址,函数的机器语言实现由载入内存的代码组成,指向函数的指针代表函数代码起始处的地址。
然后,声明函数指针时需要指明函数类型。如

void Toupper(char*);//一个函数原型
void (*pf)(char *);//pf为一个函数指针,可以指向如Toupper这样的函数类型
pf=Toupper;
char mis[]="Nine Metier";
(*pf)(mis);//语法1
pf(mis);//语法2

注意(*pf)中括号一定不能省。
有两种不同的语法用指针访问函数,这两种语法效果相同,但逻辑上不同。
(*pf)(mis):这种用法很好理解,pf是指向函数的指针,那么对它解引用就得到了这个函数本身了。
pf(mis):函数名本身是指针,注意在指针赋值时pf=Toupper,没有用&符号,说明这两种是等价的,也就可以互换使用。
最开始是历史原因,后面这两种用法都保留了下来。
通常用起来还没什么问题,但大多是时候我们并不需要用到函数的指针,真正有用的必要性也是在需要将函数指针作为某个函数的要给参数时,这时就比较容易出现混乱了。如

void show(void (*fp) (char *),char *str);

程序清单2

#include"stdio.h"
#include"stdlib.h"
#include"ctype.h"
void show(int (*changeC)(int ch),FILE* fp);
int todigit(int ch);
int notc(int ch){return ch;};
typedef enum o{notChange,toUpper,toLower,toDigit,quit} outStyle;
int main(void){
    char filename[20];
    puts("Enter a file name: ");
    scanf("%s",filename);
    FILE *fr;
    if((fr=fopen(filename,"r"))==NULL){
        printf("Can not open %s\n",filename);
        exit(EXIT_FAILURE);
    }
    puts("Enter a integer to set output style, 0 --no change, 1 --to upper, 2--to lower, 3- to digit, 4-- quit");
    //outStyle outs;
    int out;
    while(scanf("%d",&out)==1){
        switch (out)
        {
        case  notChange:
            show(notc,fr);
            break;
        case toUpper:
            show(toupper,fr);
            break;
        case toLower:
            show(tolower,fr);
            break;
        case toDigit:
            show(todigit,fr);
            break;
        default:
            puts("EXIT");
            return 0;
            break;
        }
        puts("");
    }
    return 0;
}
void show(int (*changeC)(int ch),FILE* fp){
    rewind(fp);
    char ch;
    while((ch=getc(fp))!=EOF)
        putc(changeC(ch),stdout);
}
int todigit(int ch){
    return (ch%10)+'0';
}
root@DESKTOP-624LRQ5:/mnt/d/Program Files/code/c14# ./list2
Enter a file name: 
text
Enter a integer to set output style, 0 --no change, 1 --to upper, 2--to lower, 3- to digit, 4-- quit
0
The instant you take control, interesting things will happen.
1
THE INSTANT YOU TAKE CONTROL, INTERESTING THINGS WILL HAPPEN.
2
the instant you take control, interesting things will happen.
3
4412505670621172677129106418425061415650326450352958824722106
q

13. 编程练习

  1. 设计一个结构模板存储月份名,月份名的3个字缩写,该月的天数,和月份号,定义一个数组包含12个这样的结构,初始化为一个年份(非闰年),编写一个函数,输入月份名的拼写,函数返回一年中到该月的总天数。
#include"stdio.h"
#include"stdlib.h"
#include"string.h"

struct month{
    char name[20];
    char abbr[5];
    int days;
    int indexOfMonth;
};
int getDays(int index);
struct month year[12];
int main(void){
    
    char monthName[12][20]={"January","February","March","April","May",
    "June","July","August","September","October","November","December"};
    char monthAbbr[12][5]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
    int monthDays[12]={31,28,31,30,31,30,31,31,30,31,30,31};
    int i;
    struct month mm={"asdf","fsf",1,11};
    for(i=0;i<12;i++){
        strcpy(year[i].name,monthName[i]);//初始化
        strcpy(year[i].abbr,monthAbbr[i]);
        year[i].days=monthDays[i];
        year[i].indexOfMonth=i;
    }
    int n;
    puts("Enter the month:");
    while(scanf("%d",&n)==1){
        printf("%d\n",getDays(n));
        puts("Enter the month:");
    }
    return 0;
}
int getDays(int index){
    int i,sum=0;
    if(index<0||index>12) return 0;
    for(i=0;i<index;i++){
        sum+=year[i].days;
    }
    return sum;
}
Enter the month:
23
0
Enter the month:
3
90
Enter the month:
4
120
Enter the month:
12
365
Enter the month:
q
  1. 编写一个程序,提醒用户输入日、月、年,输出这一年中到达这一天的总天数。
#include"stdio.h"
#include"stdlib.h"
#include"string.h"

struct month{
    char name[20];
    char abbr[5];
    int days;
    int indexOfMonth;
};
int getDays(int day,int mon,int years);
struct month year[12];
int main(void){
    
    char monthName[12][20]={"January","February","March","April","May",
    "June","July","August","September","October","November","December"};
    char monthAbbr[12][5]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
    int monthDays[12]={31,28,31,30,31,30,31,31,30,31,30,31};
    int i;
    struct month mm={"asdf","fsf",1,11};
    for(i=0;i<12;i++){
        strcpy(year[i].name,monthName[i]);//初始化
        strcpy(year[i].abbr,monthAbbr[i]);
        year[i].days=monthDays[i];
        year[i].indexOfMonth=i;
    }
    int d,m,y;
    puts("Enter the day month year:");
    while(scanf("%d %d %d",&d,&m,&y)==3){
        printf("%d\n",getDays(d,m,y));
        puts("Enter the day month year:");
    }
    return 0;
}
int getDays(int day,int mon,int years){
    int i,sum=0;
    int isrun=0;
    while(mon<=0){
        years--;
        mon+=12;
    }
    while(mon>12){
        years++;
        mon-=12;
    }
    if(years<0)return 0;
    if(years%100==0){
        if(years%400==0)
            isrun=1;
    }else if(years%4==0)
        isrun=1;
    if(mon<0||mon>12) return 0;
    for(i=1;i<mon;i++){
        sum+=year[i-1].days;
    }
    if(isrun && mon>2)sum++;
    return sum+day;
}
Enter the day month year:
2 2 2020
33
Enter the day month year:
3 3 2011
62
Enter the day month year:
a
  1. 修改图书目录程序,使其按照输入图书的顺序输出,然后按照书名输出,最后按照价格升序输出。
#include<stdio.h>
#include<string.h>
char * s_gets(char *st,int n);
#define MAXTITLE 40
#define MAXAUTL 40
#define MAXBKS 100
struct book{
    char title[MAXTITLE];
    char author[MAXAUTL];
    float value;
};
void swaps(struct book *b1,struct book *b2);
int main(void){
    struct book libr[MAXBKS];
    int count =0;
    int index,x,y;
    printf("Ple enter  the book title.\n");
    printf("Press [enter] at the start of a line to stop.\n");
    while(count<MAXBKS &&s_gets(libr[count].title,MAXTITLE)!=NULL && libr[count].title[0]!='\0'){
        printf("Now enter the author.\n");
        s_gets(libr[count].author,MAXAUTL);
        puts("Now ente the value.");
        scanf("%f",&libr[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",libr[index].title,libr[index].author,libr[index].value);
        puts("\nsort by the title of book:");
        //sort, 简单的用比较排序
        if(count>1)
            for(x=0;x<count-1;x++)
                for(y=x+1;y<count;y++)
                    if(strcmp(libr[x].title,libr[y].title)>0)
                        swaps(libr+x,libr+y);
        for(index=0;index<count;index++)
            printf("%s by %s : $%.2f \n",libr[index].title,libr[index].author,libr[index].value);
        puts("\nsort by the value of book:");
        //sort, 简单的用比较排序
        if(count>1)
            for(x=0;x<count-1;x++)
                for(y=x+1;y<count;y++)
                    if(libr[x].value>libr[y].value)
                        swaps(libr+x,libr+y);
        for(index=0;index<count;index++)
            printf("%s by %s : $%.2f \n",libr[index].title,libr[index].author,libr[index].value);
    }
    else
        printf("No books? Too bad.\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';
        else
            while(getchar()!='\n')
                continue;
    }
    return ret_val;
}
void swaps(struct book *b1,struct book *b2){  //交换两个结构变量
    if(b1==b2)return;
    struct book temp=*b2;
    strcpy(b2->title,b1->title);
    strcpy(b2->author,b1->author);
    b2->value=b1->value;
    strcpy(b1->title,temp.title);
    strcpy(b1->author,temp.author);
    b1->value=temp.value;
}
root@DESKTOP-624LRQ5:/mnt/d/Program Files/code/c14# ./prac3
Ple enter  the book title.
Press [enter] at the start of a line to stop.
vocabulary builder
Now enter the author.
Webster's
Now ente the value.
11.1
Enter the next title.
Quantum Filed Theory
Now enter the author.
weinberg
Now ente the value.
99.9
Enter the next title.
Foundation of Quantum Programming 
Now enter the author.
Mingsheng Ying
Now ente the value.
77.7
Enter the next title.

Here is the list of your books:
vocabulary builder by Webster's : $11.10
Quantum Filed Theory by weinberg : $99.90
Foundation of Quantum Programming by Mingsheng Ying : $77.70

sort by the title of book:
Foundation of Quantum Programming by Mingsheng Ying : $77.70
Quantum Filed Theory by weinberg : $99.90
vocabulary builder by Webster's : $11.10

sort by the value of book:
vocabulary builder by Webster's : $11.10 
Foundation of Quantum Programming by Mingsheng Ying : $77.70
Quantum Filed Theory by weinberg : $99.90
  1. 编写一个程序创建一个结构模板,编写要给函数打印结构变量。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

struct member{
    char id[20];
    struct {
        char firstName[20];
        char midName[20];
        char lastName[20];
    };
};
void show(struct member* m,int n);
int main(void){
    struct member mems[5];
    int ind;
    for(ind=0;ind<5;ind++){
        printf("Enter the %d th member's id firstname midname lastname: \n",ind);
        while(scanf("%s %s %s %s",mems[ind].id,mems[ind].firstName,mems[ind].midName,mems[ind].lastName)!=4)
            puts("Enter it again");
    }
    puts("list of members :");
    show(mems,5);
    return 0;
}
void show(struct member* m,int n){
    int i=0;
    for(i=0;i<n;i++){
        printf("%s ,%s  %s -- %s\n",m[i].firstName,m[i].midName,m[i].lastName,m[i].id);
    }
}
Enter the 0 th member's id firstname midname lastname: 
asdf sadf sa s 
Enter the 1 th member's id firstname midname lastname: 
sdf dfds dsr t tgf
Enter the 2 th member's id firstname midname lastname: 
dfs dsf sdfg dsgf
Enter the 3 th member's id firstname midname lastname: 
sdfg sdfgsdf g
Enter the 4 th member's id firstname midname lastname: 
dsf gdgh fhn
sd fer
list of members :
sadf ,sa  s -- asdf
dfds ,dsr  t -- sdf
dfs ,dsf  sdfg -- tgf
sdfg ,sdfgsdf  g -- dsgf
gdgh ,fhn  sd -- dsf
#include"stdio.h"
#include"stdlib.h"
#include"string.h"
#define CSIZE 4
struct name{char firstname[20];char lastname[20]; };
struct student{struct name Name;double grade[3];double mean;};
int main(void){
    struct student stus[CSIZE];
    int i;
    for(i=0;i<CSIZE;i++){
        printf("Student %d, Enter the name ,format: firstname last name\n",i);//交互式输入学生信息
        while(scanf("%s %s",stus[i].Name.firstname,stus[i].Name.lastname)!=2)puts("Enter it again");
        puts("Enter the grade,format : grade 1,grade2,grade3");
        while(scanf("%lf,%lf,%lf",stus[i].grade,stus[i].grade+1,stus[i].grade+2)!=3)puts("Enter it again");
    }
    for(i=0;i<CSIZE;i++)
        stus[i].mean=(stus[i].grade[0]+stus[i].grade[1]+stus[i].grade[2])/3.0;
    for(i=0;i<CSIZE;i++)
        printf("Name: %s %s, grade: %.1lf,%.1lf,%.1lf, mean:%.1lf\n",stus[i].Name.firstname,stus[i].Name.lastname,stus[i].grade[0],stus[i].grade[1],stus[i].grade[2],stus[i].mean);
    return 0;
}
Student 0, Enter the name ,format: firstname last name
asd sd 
Enter the grade,format : grade 1,grade2,grade3
45,66,78  
Student 1, Enter the name ,format: firstname last name
asd asd
Enter the grade,format : grade 1,grade2,grade3
656,78,97
Student 2, Enter the name ,format: firstname last name
asd sad
Enter the grade,format : grade 1,grade2,grade3
54,66,88
Student 3, Enter the name ,format: firstname last name
asd s 
Enter the grade,format : grade 1,grade2,grade3
77,66,88
Name: asd sd, grade: 45.0,66.0,78.0, mean:63.0   
Name: asd asd, grade: 656.0,78.0,97.0, mean:277.0
Name: asd sad, grade: 54.0,66.0,88.0, mean:69.3  
Name: asd s, grade: 77.0,66.0,88.0, mean:77.0
  1. 编写一个程序,通过函数指针实现swith。
#include<stdio.h>
#include<stdlib.h>

void fun1(void);
void fun2(void);
void fun3(void);
void fun4(void);
void fun5(void);

int main(void){
    void (*pf [5])(void);//函数指针
    pf[0]=fun1;
    pf[1]=fun2;
    pf[2]=fun3;
    pf[3]=fun4;
    pf[4]=fun5;
    char ch;
    int ind=0;
    puts("Enter a char to select a function to run:");
    while((ch=getchar())!=EOF){
        if(ch>'d'||ch<'a'){
            if(ch!='\n')
                pf[4]();
            continue;
        }
        ind=ch-'a';
        pf[ind]();//运行函数
        getchar();
    }
    return 0;
}
void fun1(void){
    puts("This is fun1");
}
void fun2(void){
    puts("This is fun2");
}
void fun3(void){
    puts("This is fun3");
}
void fun4(void){
    puts("This is fun4");
}
void fun5(void){
    puts("default func");
}
Enter a char to select a function to run:
a
This is fun1
c
This is fun3
d
This is fun4
f
default func
s
default func
w
default func
a
This is fun1
root@DESKTOP-624LRQ5:/mnt/d/Program Files/code/c14# 

用函数指针来实现判断语句完全时可行的,但用起来很不方便,可读性也差,所以不实用。
循环语句也基本上可以通过递归函数是实现,循环和判断语句很多时候都不是不可缺少的。
但想想,我们写一个实现某个特定功能的程序时,如果坚持不用循环和判断语句,用递归函数和函数指针代替,那么最终也许能够很好的实现需要的功能,但程序本身就可读性就不好,这样还是会带来一些后续的问题的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值