《C语言程序设计 谭浩强 第4版》:使用结构体类型处理组合数据 - 用户自定义数据类型

一、定义和使用结构体变量

1、自己建立结构体类型 

C 语言允许用户自己定义由不同类型数据组成的组合型的数据结构,它称为 结构体 

一般形式: 

struct 结构体名{
    成员表列;
}

结构体类型的名字是由一个关键字 struct 和结构体名二者组合而成的 

结构体名由用户指定,又称 “结构体标记”,以区别于其他的结构体类型 

大括号内是该结构体所包括的子项,称为结构体的成员,对各成员都应进行类型声明 

类型名 成员名;

"成员表列" 也称为 “域表”,每一个成员是结构体的一个域 

成员名命名规则与变量名相同 

成员可以是另一个结构体变量 

 

2、定义结构体类型变量 

1)先声明结构体类型,再定义该类型的变量 

在定义了结构体变量后,系统会为之分配内存单元 

 

2)在声明类型的同时定义变量 

struct 结构体名{
    成员表列;
}变量名表列;

 

3)不指定类型名而直接定义结构体类型变量 

struct{
    成员表列;
}变量名表列;
  • 结构体类型与结构体变量是不同的。只能对变量赋值、存取或运算,而不能对一个类型赋值、存取或运算。在编译时,对类型是不分配空间的,只对变量分配空间
  • 结构体类型中的成员可以与程序中的变量名相同,但二者不代表同一对象
  • 对结构体变量中的成员,可以单独使用,它的作用与地位相当于普通变量 

 

3、结构体变量的初始化和引用 

把一个学生的信息放在一个结构体变量中,然后输出这个学生的信息 

#include <stdio.h>

int main(){
    struct student{
        int num;
        char name[20];
        char sex;
        char addr[20];
    } student1 = {10101,"Li Lin",'M',"123 BeiJing Road"};
 
    printf("No.:%d\nname:%s\nsex:%c\naddress:%s\n", student1.num, student1.name, student1.sex, student1.addr);
    return 0;
}

引用结构体变量应遵循以下规则: 

  • 可以引用结构体变量中成员的值,引用方式: 
    • 结构体变量名.成员名
    • 不能通过结构体变量名来得到结构体变量中所有成员的值 
  • 如果成员本身又属于一个结构体类型,则要用若干个成员运算符,一级一级的找到最低一级的成员
  • 对结构体变量的成员可以像普通变量一样进行各种运算(“ . ”运算符的优先级最高)
  • 同类的结构体变量可以互相赋值
  • 可以引用结构体变量成员的地址,也可以引用结构体变量的地址
// 正确用法
// 输入 student.num 的值
scanf("%d",&student1.num);

// 输出结构体变量 student1 的首地址
printf("%o",&student1);

// 错误用法
scanf("%d,%s,%c,%d,%f,%s",&studen1);

 

二、结构体数组 

每一个数组元素都是一个结构体类型的数据的数组称为 结构体数组

 

有 3 个候选人,每个选民只能投票选一人,要求编写一个统计选票的程序,先后输入被选人的名字,最后输出各人得票结果 

#include <string.h>
#include <stdio.h>

struct person{
    char name[20];
    int count;
} leader[3] = {"Li",0,"Zhang",0,"Fun",0};

int main(){
    int i,j;
    char leader_name[20];
    for(i = 1;i < 10;i++){
        scanf("%s",leader_name);
        for(j = 0;j < 3;j++){
            if(strcmp(leader_name,leader[j].name) == 0){
                leader[j].count++;
            }
        }
    }

    printf("\nResult:\n");

    for(i = 0;i < 3;i++){
        printf("%5s:%d\n",leader[i].name,leader[i].count);
    }

    return 0;
}

定义结构体数组一般形式 

{成员表列} 数组名[数组长度];
结构体类型 数组名[数组长度];
struct person leader[3];

对结构体数组初始化的形式是在定义数组的后面加上 

= {初值表列};

 

三、结构体指针 

结构体指针,指向结构体数据的指针,一个结构体变量的起始地址就是这个结构体变量的指针 

指针变量既可以指向结构体变量,也可以用来指向结构体数组中的元素。但是指针变量的基类型必须与结构体变量的类型相同 

 

通过指向结构体变量的指针变量输出结构体变量中成员的信息 

#include <stdio.h>
#include <string.h>

int main(){
    struct student{
        long num;
        char name[20];
        char sex;
        float score;
    }; // 逗号不能漏掉!!!!!!!!!!!!

    struct student stu_1;
    struct student *p;

    p = &stu_1;
    stu_1.num = 10101;
    strcpy(stu_1.name,"Li Lin");
    stu_1.sex = 'M';
    stu_1.score = 89.5;

    printf("No.:%ld\nname:%s\nsex:%c\nscore:%5.1f\n",stu_1.num,stu_1.name,stu_1.sex,stu_1.score);
    // *p = stu_1
    printf("\nNo.:%ld\nname:%s\nsex:%c\nscore:%5.1f\n",(*p).num,(*p).name,(*p).sex,(*p).score);

    return 0;
}

为了使用方便和使之直观,C 语言允许直接把 p->num 代替 (*p).num ,它表示 p 所指向的结构体变量中的 num 成员

如果 p 指向一个结构体变量,以下 3 种形式等价

  • 结构体变量.成员名
  • (*p).成员名
  • p -> 成员名

 

有 3 个学生的信息,放在结构体数组中,要求输出全部学生的信息 

#include <stdio.h>

struct student{
    int num;
    char name[20];
    char sex;
    int age;
};

struct student stu[3] = {
    {10101,"Li Lin",'M',18},
    {10102,"Zhang Fun",'M',19},
    {10103,"Wang Min",'F',18}
};

int main(){
    struct student *p;
    printf("No. Name sex age\n");
    for(p = stu;p < stu + 3;p++){
        printf("%-23d %-21s %3c %24d\n",p->num,p->name,p->sex,p->age);
    }

    return 0;
}

运行结果:

No.              Name         sex         age
10101         Li Lin            M           18
10102         Zhang Fun   M           19
10103         Wang Min     F           18 

如果 p 的初值为 stu,即 p 指向 stu 的第一个元素,p 加 1 后,p 就指向下一个元素 

p 是一个指向 struct student 类型数据的指针变量,它用来指向一个 struct student 类型的数据的起始地址,不应用来指向 stu 数组元素中的某一成员 

 

四、用结构体变量和结构体变量的指针作函数参数 

将一个结构体变量的值传递给一个函数,有 3 种方法 

  • 用结构体变量的成员作参数
    • 属于值传递
    • 应注意实参与形参的类型保持一致
  • 用结构体变量作参数
    • 属于值传递
  • 用指向结构体变量(或数组)的指针作实参,将结构体变量(或数组)的地址传给形参 

 

有 N 个结构体变量 stu,内含学号、姓名和 3 门课程的成绩。要求输出平均成绩最高的学生的信息(包括学号、姓名、3 门课程成绩和平均成绩) 

#include <stdio.h>

#define N 3

struct student{
    int num;
    char name[20];
    float score[3];
    float aver;
};

int main(){
    void input(struct student stu[]);

    struct student max(struct student stu[]);

    void print(struct student stu);

    struct student stu[N],*p = stu;

    input(p);

    print(max(p));

    return 0;
}

void input(struct student stu[]){
    int i;
    printf("请输入各学生的信息:学好、姓名、三门课程成绩!\n");
    for(i = 0;i < N;i++){
        scanf("%d %s %f %f %f", &stu[i].num,stu[i].name,&stu[i].score[0], &stu[i].score[1],&stu[i].score[2]);
        stu[i].aver = (stu[i].score[0] + stu[i].score[1] + stu[i].score[2]) / 3.0;
    }
}

struct student max(struct student stu[]){
    int i,m = 0;
    for(i = 0;i < N;i++){
        if(stu[i].aver > stu[m].aver){
            m = i;
        }
    }
    return stu[m];
}

void print(struct student stud){
    printf("\n成绩最高的学生是:\n");
    printf("学好:%d\n姓名:%s\n三门课程成绩:%5.1f,%5.1f,%5.1f\n平均成绩:%6.2f\n",stud.num,stud.name,stud.score[0],stud.score[1],stud.score[2],stud.aver);
}

运行结果:
请输入各学生的信息:学好、姓名、三门课程成绩!
10101 LiLin 78 89 98
10102 WangFun 98.5 87 69
10103 WeiMin 88 76.5 89
成绩最高的学生是:
学好:10101
姓名:LiLin
三门课程成绩: 78.0, 89.0, 98.0
平均成绩: 88.33 

 

五、用指针处理链表 

1、什么是线性链表 

静态的数据结构: 

分配固定的存储单元,在内存中连续存放(如数组) 

动态的数据结构:

没有固定大小,根据需要随时开辟存储单元,用完后可以随时释放存储单元(线性链表) 

 

链表有一个 “头指针” 的变量,存放一个地址,该地址指向链表中的第一个元素 

 

链表中的各元素称为 “结点”,每个结点都应包括两个部分: 

  • 用户需要用的实际数据
  • 下一个结点的地址 

 

链表中各结点在内存中的地址可以是不连续的 

链表结构体类型: 

struct student{
    int num;
    float score;
    struct student *next;
}

 

2、建立简单的静态链表 

所有结点都是成程序中定义的,不是临时开辟的,也不能用完后释放,这种链表称为“ 静态链表 ” 

 

建立如图所示的简单链表,由 3 个学生数据的结点组成。输出各结点中的数据 

#include <stdio.h>

#define NULL 0

struct student{
    int num;
    float score;
    struct student *next;
};

int main(){
    struct student a,b,c,*head,*p;

    a.num = 10101;
    a.score = 89.5;

    b.num = 10102;
    b.score = 90;

    c.num = 10103;
    c.score = 85;

    head = &a;
    a.next = &b;
    b.next = &c;
    c.next = NULL;
    p = head;

    do{
    printf("%ld %5.1f\n",p -> num,p -> score);
    p = p -> next;
    } while(p != NULL);

    return 0;
}

 

3、建立动态链表 

所谓动态链表是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结点和输入各结点数据,并建立起前后相连的关系 

 

建立一个有两名学生学号和成绩数据的单向动态链表 

#include <stdio.h>
#include <malloc.h>

#define LEN sizeof(struct student)

struct student{
    int num;
    float score;
    struct student *next;
};

int main(){
    struct student *head,*p;
    head = p = (struct student *) malloc(LEN);
    scanf("%d %f",&p -> num,&p -> score);
    p = (struct student *)malloc(LEN);
    scanf("%d %f",&p -> num,&p -> score);
    head -> next = p;
    p -> next = NULL;
    p = head;
    printf("\n结点1:%d,%6.2f\n",p -> num,p -> score);
    p = p -> next;
    printf("结点2:%d,%6.2f\n",p -> num,p -> score);
    return 0;
}

 

六、提高

1、共用体类型 

几个不同的变量共占同一段内存的结构,称为 “ 共用体 ” 类型的结构 

一般形式: 

union 共用体名{
    成员表列;
} 变量表列;

 结构体变量所占内存长度是各成员占的内存长度之和,每个成员分别占有其自己的内存单元 

共用体变量所占内存长度等于最长的成员的长度 

不用引用共用体变量,只能引用共用体变量中的成员 

 

2、枚举类型 

如果一个变量只有几种可能的值,则可以定义为 枚举类型 

“枚举” 就是将变量值一一列举出来,变量的值只限于列举出来的值的范围内 

 

声明枚举类型: 

enum weekday{
    sun,mon,tue,wed,thu,fri,sat
};

定义枚举变量: 

enum weekday workday,week_end;

workday,week_end 即为枚举变量,其值在 sun 到 sat 之间的一个 

其中 sun …… sat 称为 枚举元素枚举常量 

 

口袋中有 红、黄、蓝、白、黑 5 种颜色的球若干个。每次从口袋中先后取出 3个球,问得到 3 种不同的的球的可能排列,输出每种排列的情况 

#include <stdio.h>

int main(){
    enum color{
    red,
    yellow,
    blue,
    white,
    black
    };
    enum color i,j,k,pri;
    int n,loop;
    n = 0;
    for(i = red;i <= black;i++){
        for(j = red;j <= black;j++){
            if(i != j){
                for(k = red;k <= black;k++){
                    if((k != i) && (k != j)){
                        n = n + 1;
                        printf("%-4d",n);
                        for(loop = 1;loop <= 3;loop++){
                            switch(loop){
                                case 1:
                                    pri = i;
                                    break;
                                case 2:
                                    pri = j;
                                    break;
                                case 3:
                                    pri = k;
                                    break;
                                default:
                                    break;
                            } // switch(loop) END

                            switch(pri){
                                case red:
                                    printf("%-10s","red");
                                    break;
                                case yellow:
                                    printf("%-10s","yellow");
                                    break;
                                case blue:
                                    printf("%-10s","blue");
                                    break;
                                case white:
                                    printf("%-10s","white");
                                    break;
                                case black:
                                    printf("%-10s","black");
                                    break;
                                default:
                                    break;
                            } // switch(pri) END
                        } // for(loop = 1;loop <=3;loop++) END
                        printf("\n");
                    } // if((k != i) && (k != j)) END
                } // for(k = red;k <= black;k++) END
            } // if(i != j) END
        } // for(j = red;j <= black;j++) END
    } // for(i = red;i <= black;i++) END
    printf("\ntotal : %5d\n",n);
    return 0;
} // int main() END

运行结果
1 red yellow blue
2 red yellow white
3 red yellow black
…… …… …… ……
58 black white red
59 black white yellow
60 black white blue
total : 60 

 

一  叶  知  秋,奥  妙  玄  心

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

QX_Java_Learner

祝老板生意兴隆,财源广进!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值