开讲啦:Chap 09 用户自己建立数据类型

简介: C语言允许用户根据需要自己建立数据类型,用它来定义变量

前言


C语言允许用户根据需要自己建立数据类型,用它来定义变量。


9.1 定义和使用结构体变量

9.1.1 自己建立结构体类型

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


struct Student{
    int num;
    char name[20];
    char sex;
    int  age;
    float score;
    char addr[30];
};


声明一个结构体的一般形式为:


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


  • 成员可以属于另一个结构体类型。


struct Date{
    int month;
    int year;
    int day;
};
struct Student{
    int num;
    char name[20];
    chat sex;
    int age;
    struct Date birthday;
    char addr[30];
};


9.1.2 定义结构体类型变量


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


struct Student{
    int num;
    char name[20];
    char sex;
    int  age;
    float score;
    char addr[30];
};
struct Student student1,student2;


int a,b;类似,定义完成后,student1student2即为struct Student类型的变量。


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


struct Student{
    int num;
    char name[20];
    char sex;
    int  age;
    float score;
    char addr[30];
} student1,student2;


其一般形式为:


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


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


struct{
  //成员列表
}变量名列表;


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


例9.1 把一个学生的学号、姓名、性别、住址放在一个结构体变量中,然后输出这个学生的信息。


#include<stdio.h>
int main(){
    struct Student{
        int num;
        char name[20];
        char sex;
        char addr[20];
    }student = {100001,"shipudong",'M',"China Xi'an"};
    printf("学号:%d\n姓名:%s\n性别:%c\n地址:%s\n",student.num,student.name,student.sex,student.addr);
    return0;
}


  • C99标准允许对某一成员初始化,其他未被指定初始化的数值型成员被系统初始化为0,字符型成员被系统初始化为\0,指针型成员被系统初始化为NULL


#include<stdio.h>
int main(){
    struct Student{
        int num;
        char name[20];
        char sex;
        char addr[20];
    }student = {100001,"shipudong",'M',"China Xi'an"};
    struct Student student1 = {.name = "hahaCoder"};
    printf("学号:%d\n姓名:%s\n性别:%c\n地址:%s\n",student.num,student.name,student.sex,student.addr);
    printf("==========================\n");
    printf("学号:%d\n姓名:%s\n性别:%c\n地址:%s\n",student1.num,student1.name,student1.sex,student1.addr);
    return0;
}


  • 可以通过结构体变量名.成员名引用结构体变量中成员的值,不能企图输出结构体变量名来达到输出结构体变量所有成员的值;
  • 如果成员本身又属一个结构体类型,则要用若干个成员运算符,一级一级地找到最低的一级的成员。


#include<stdio.h>
int main(){
    struct Date{
        int year;
        int month;
        int day;
    };
    struct Student{
        int num;
        char name[20];
        char sex;
        struct Date birthday;
        char addr[20];
    }student = {100001,"shipudong",'M',{2021,10,11},"China Xi'an"};
    struct Student student1 = {.name = "hahaCoder"};
    printf("学号:%d\n姓名:%s\n性别:%c\n出生日期:%d年%d月%d日\n地址:%s\n",student.num,student.name,student.sex,student.birthday.year,student.birthday.month,student.birthday.day,student.addr);
    printf("==========================\n");
    printf("学号:%d\n姓名:%s\n性别:%c\n出生日期:%d年%d月%d日\n地址:%s\n",student1.num,student1.name,student1.sex,student1.birthday.year,student1.birthday.month,student1.birthday.day,student1.addr);
    return0;
}


  • 对结构体变量的成员可以像普通变量一样进行各种运算
  • 同类的结构体变量可以互相赋值;


#include<stdio.h>
int main(){
    struct Date{
        int year;
        int month;
        int day;
    };
    struct Student{
        int num;
        char name[20];
        char sex;
        struct Date birthday;
        char addr[20];
    }student = {100001,"shipudong",'M',{2021,10,11},"China Xi'an"};
    struct Student student1 = {.name = "hahaCoder"};
    printf("学号:%d\n姓名:%s\n性别:%c\n出生日期:%d年%d月%d日\n地址:%s\n",student.num,student.name,student.sex,student.birthday.year,student.birthday.month,student.birthday.day,student.addr);
    printf("==========================\n");
    printf("学号:%d\n姓名:%s\n性别:%c\n出生日期:%d年%d月%d日\n地址:%s\n",student1.num,student1.name,student1.sex,student1.birthday.year,student1.birthday.month,student1.birthday.day,student1.addr);
    student1 = student;
    printf("==========================\n");
    printf("学号:%d\n姓名:%s\n性别:%c\n出生日期:%d年%d月%d日\n地址:%s\n",student.num,student.name,student.sex,student.birthday.year,student.birthday.month,student.birthday.day,student.addr);
    return0;
}


  • 可以引用结构体变量成员的地址,也可以引用结构体变量的地址,结构体变量的地址主要用作函数参数,传递结构体变量的地址;


例9.2 输入两个学生的学号、姓名和成绩,输出成绩较高的学生的学号、姓名和成绩。


#include<stdio.h>
int main(){
    struct Student{
        int num;
        char name[20];
        float score;
    }student1,student2;
    printf("请分别输入两个学生的学号、姓名和分数:\n");
    scanf("%d %s %f",&student1.num,student1.name,&student1.score);
    scanf("%d %s %f",&student2.num,student2.name,&student2.score);
    printf("成绩较高的学生为:\n");
    if(student1.score>student2.score){
        printf("%d %s %f\n",student1.num,student1.name,student1.score);
    }elseif (student1.score<student2.score){
        printf("%d %s %f\n",student2.num,student2.name,student2.score);
    }else{
        printf("%d %s %.2f\n",student1.num,student1.name,student1.score);
        printf("%d %s %.2f\n",student2.num,student2.name,student2.score);
    }
    return0;
}


9.2 使用结构体数组


9.2.1 定义结构体数组


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


#include<stdio.h>
#include<string.h>
struct Person{
    char name[20];
    int count;
}leader[3] = {"hahaCoder",0,"hahaAI",0,"hahaWebsite.",0};
int main(){
    char leader_name[20];
    for (int i = 1; i <= 8; i++) {
        printf("请输入第%d个候选人姓名:",i);
        scanf("%s",leader_name);
        for (int j = 0; j < 3; j++) {
            if(strcmp(leader_name, leader[j].name)==0){
                leader[j].count++;
            }
        }
    }
    for (int i = 0; i < 3; i++) {
        printf("姓名:%s --> 选票数:%d\n",leader[i].name,leader[i].count);
    }
    return0;
}


定义结构体数组的一般形式为:


struct 结构体名{
  成员列表;
}数组名[数组长度];


9.2.2 结构体数组的应用举例


例9.4 有n个学生的信息(包括学号、姓名、成绩),要求按照成绩的高低顺序输出各学生的信息。


#include<stdio.h>
struct Student{
    int num;
    char name[20];
    float score;
};
int main(){
    struct Student student[5] = {
        {0001,"hahaAI",98},
        {0002,"hahaCoder",99},
        {0003,"hahaWebsite",90},
        {0004,"hahaOCR",89},
        {0005,"hahaBook",100}
    };
    struct Student temp;
    int i,j,k;
    constint n = 5;
    printf("得分由小到大的顺序为:\n");
    for (i = 0; i < n-1; i++) {
        k = i;
        for (j = i+1; j < n; j++) {
            if(student[j].score < student[k].score){
                k = j;
            }
        }
        if(k!=i){
            temp = student[k];
            student[k] = student[i];
            student[i] = temp;
        }
    }
    for (int i = 0; i < n; i++) {
        printf("学号:%d\t姓名:%s\t得分:%.2f\n",student[i].num,student[i].name,student[i].score);
    }
    return0;
}


9.3 结构体指针


所谓结构体指针就是指向结构体变量的指针,一个结构体变量的起始地址就是这个结构体变量的指针。如果把一个结构体变量的起始地址存放在一个指针变量中,那么,这个指针变量就指向该结构体变量。


9.3.1 指向结构体变量的指针


指向结构体对象的指针变量既可指向结构体变量,也可指向结构体数组中的元素,指针变量的基类型必须与结构体变量的类型相同。



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


#include<stdio.h>
#include<string.h>
int main(){
    struct Student{
        int num;
        char name[20];
        char sex;
        float score;
    }student,*p;
    p = &student;
    student.num = 100001;
    strcpy(student.name, "hahaCoder");
    student.sex = 'M';
    student.score = 98.29;
    printf("学号:%d\t姓名:%s\t性别:%c\t分数:%.2f\n",student.num,student.name,student.sex,student.score);
    printf("学号:%d\t姓名:%s\t性别:%c\t分数:%.2f\n",(*p).num,(*p).name,(*p).sex,(*p).score);
    printf("学号:%d\t姓名:%s\t性别:%c\t分数:%.2f\n",p->num,p->name,p->sex,p->score);
    return0;
}


如果p指向一个结构体变量stu,以下3种方法等价:


  • stu.成员名stu.num
  • (*p).成员名(*p).num
  • p->成员名p->num


9.3.2 指向结构体数组的指针


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


#include<stdio.h>
struct Student{
    int num;
    char name[20];
    float score;
};
struct Student student[5] = {
    {0001,"hahaAI",98},
    {0002,"hahaCoder",99},
    {0003,"hahaWebsite",90},
    {0004,"hahaOCR",89},
    {0005,"hahaBook",100}
};
int main(){
    struct Student *p;
    for (p = student; p < student+5; p++) {
        printf("学号:%d\t姓名:%s\t分数:%.2f\n",p->num,p->name,p->score);
    }
    return0;
}

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

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


  • 用结构体变量的成员作参数;
  • 用结构体变量作实参;
  • 用指向结构体变量或数组元素的指针作实参,将结构体变量或数组元素的地址传给形参;


例9.7 有n个结构体变量,内含学生学号、姓名和3门课的成绩,要求求出平均成绩最高的学生的信息。


#include<stdio.h>
#define NUM 3
struct Student{
    int num;
    char name[20];
    float score[3];
    float aver;
};
int main(){
    void input(struct Student student[]);
    struct Student max(struct Student student[]);
    void print(struct Student student);
    struct Student student[NUM],*p = student;
    input(p);
    print(max(p));
    return0;
}
void input(struct Student student[]){
    for (int i = 0; i < NUM; i++) {
        printf("请输入第%d个学生的学号、姓名、三门课成绩等信息:\n",i+1);
        scanf("%d %s %f %f %f",&student[i].num,student[i].name,&student[i].score[0],&student[i].score[1],&student[i].score[2]);
        student[i].aver = (student[i].score[0]+student[i].score[1]+student[i].score[2])/3.0;
    }
}
struct Student max(struct Student student[]){
    int max_num = 0;
    for (int i = 0; i < NUM; i++) {
        if(student[i].aver > student[max_num].aver){
            max_num = i;
        }
    }
    return student[max_num];
}
void print(struct Student student){
    printf("成绩最高的学生为:\n");
    printf("学号:%d\t姓名:%s\t三门课成绩分别为:%.2f、%.2f、%.2f\t平均成绩:%.2f\n",student.num,student.name,student.score[0],student.score[1],student.score[2],student.aver);
}


9.4 用指针处理链表


9.4.1 什么是链表


微信图片_20220611044405.png


链表有一个头指针变量,图中以head表示,它存放一个地址,该地址指向一个元素,链表中每一个元素称为结点,每个结点都应该包括两个部分:


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


可以看出,head指向第1个元素,第1个元素又指向第2个元素......直到最后一个元素,该元素不再指向其他元素,它称为表尾,它的地址部分放一个NULL,表示空地址,链表到此结束。链表中各元素在内存中的地址可以是不连续的,要找某一元素,必须先找到上一个元素,根据它提供的下一元素地址才能找到下一个元素,如果不提供头指针,则整个链表无法访问。


9.4.2 建立简单的静态链表


参考试听课代码。


9.4.3 建立动态链表


参考试听课代码。


9.4.4 输出链表


参考试听课代码。


9.5 共用体类型


9.5.1 什么是共用体类型


定义共用体类型的一般形式为:


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


共用体与结构体定义形式类似,但它们的含义是不同的,结构体变量所占内存长度是各成员占的内存长度之和,每个成员分别占有其自己的内存单元,而共用体变量所占的内存长度等于最长的成员的长度。


#include<stdio.h>
struct Student{
    int num;
    char name[20];
    float score[3];
    float aver;
};
union Person{
    int num;
    char name[20];
    float score[3];
    float aver;
};
int main(){
    printf("结构体Student的大小为:%d\n",sizeof(struct Student)); // 40
    printf("共用体union的大小为:%d\n",sizeof(union Person)); // 20
    return0;
}

9.5.2 引用共用体变量的方式

#include<stdio.h>
union Person{
    int num;
    char name[20];
    float aver;
} person;
int main(){
    person.num = 1000;
    printf("学号:%d\t姓名:%s\t平均分:%.2f\n",person.num,person.name,person.aver);
    return0;
}

9.5.3 共用体类型数据的特点

  • 同一个内存段可以用来存放几种不同类型的成员,但在每一瞬时只能存放其中一个成员,而不是同时存放几个;
  • 可以对共用体变量初始化,但初始化列表中只能有一个常量;
  • 共用体变量中起作用的成员是最后一次被赋值的成员,在对共用体变量中的一个成员赋值后,原有变量存储单元中的值就被取代;


共用体类型的数据的应用场景:有时需要对同一段空间安排不同的用途。


例9.8 有若干个人员的数据,其中有学生和教师,学生的数据中包括:姓名、号码、性别、职业、班级;教师的数据包括:姓名、号码、性别、职业、职务。


#include<stdio.h>
struct{
    int num;
    char name[10];
    char sex;
    char job;
    union{
        int clas;
        char position[10];
    }category;
}person[2];
int main(){
    int i;
    for (i = 0; i < 2; i++) {
        printf("请输入第%d个用户的信息:\n",(i+1));
        scanf("%d %s %c %c",&person[i].num,person[i].name,&person[i].sex,&person[i].job);
        if(person[i].job=='s'){
            printf("请输入学生班级信息:\n");
            scanf("%d",&person[i].category.clas);
        }elseif (person[i].job == 't'){
            printf("请输入老师职务信息:\n");
            scanf("%s",person[i].category.position);
        }else{
            printf("输入错误!\n");
        }
    }
    for (i = 0;i < 2; i++) {
        if(person[i].job == 's'){
            printf("学号:%d\t姓名:%s\t性别:%c\t职业:%c\t班级:%d\n",person[i].num,person[i].name,person[i].sex,person[i].job,person[i].category.clas);
        }else{
            printf("号码:%d\t姓名:%s\t性别:%c\t职业:%c\t职务:%s\n",person[i].num,person[i].name,person[i].sex,person[i].job,person[i].category.position);
        }
    }
    return0;
}

9.6 使用枚举类型

所谓枚举就是把可能的值一一列举出来,变量的值只限于列举出来的值的范围内,声明枚举类型用enum开头,其一般形式为:enum [枚举名]{枚举元素列表};


  • C编译对枚举类型的枚举元素按常量处理,故不能对它们赋值;
  • 每一个枚举元素都代表一个整数,C语言编译按定义时的顺序默认它们的值为0、1、2、3、4...;
  • 枚举元素可以用来做比较;


例9.9 枚举类型案例


#include <stdio.h>
enum DAY
{
      MON, TUE, WED, THU, FRI, SAT, SUN
};
int main()
{
    enum DAY day;
    day = WED;
    printf("%d\n",day);
    return0;
}
  • 9.7 用typedef声明新类型


  • 简单地用一个新的类型名代替原有的类型名


#include <stdio.h>
int main(){
//  int a = 10;
    typedefint shipudongInt;
    shipudongInt a = 10;
    printf("%d\n",a);
    return0;
}


  • 命名一个简单的类型名代替复杂的类型表示方法


  • 命名一个新的类型名代表结构体类型


typedefstruct{
  int month;
  int day;
  int year;
}Date;
Date birthday;


  • 命名一个新的类型名代表数组类型


typedefint NUM[100];
NUM a;


  • 命名一个新的类型名代表指针类型


typedefchar* string;
string p;


  • 命名一个新的类型名代表指向函数的指针类型


typedef int (*pointer)();//pointer为指向函数的指针类型,该函数返回整型值;
pointer p1,p2;//p1、p2为pointer类型的指针变量
相关文章
|
4月前
ASA对接 LDAP ,少不了这些关键配置。
ASA对接 LDAP ,少不了这些关键配置。
|
7月前
|
网络协议 网络架构
【计网·湖科大·思科】实验六 IP数据报的发送和转发流程、默认路由和特定主机路由
【计网·湖科大·思科】实验六 IP数据报的发送和转发流程、默认路由和特定主机路由
242 0
|
网络协议 网络架构
二十九、IP数据报
二十九、IP数据报
二十九、IP数据报
|
存储 C语言
开讲啦:Chap 06 利用数组处理批量数据
数组:同一类性质的数据 数组是一组有序数据的集合,数组中各数据的排列是有一定规律的,下标代表数据在数组中的序号。;用一个数组名和下标来唯一地确定数组中的元素;数组中的每一个元素都属于同一个数据类型,不能把不同类型的数据(如学生的成绩和学生的性别)放在同一个数组中。
开讲啦:Chap 06 利用数组处理批量数据
|
存储 SQL 监控
计算机二级考试公共基础知识部分——-数据库
计算机二级考试公共基础知识部分——-数据库
计算机二级考试公共基础知识部分——-数据库
|
安全 数据安全/隐私保护
国内电子文件系统密码应用规范即将建立
日前笔者从国家密码管理局获悉,《安全电子文件密码应用规范》已经制订完毕并通过验收,将在2009年初开始实施。该规范标准的制定有利于电子文件应用系统密码使用的规范化、密码服务产品的标准化,有助于提高电子文件应用的安全性和系统之间安全电子文件的互通性,对安全电子文件应用产品的发展具有重要的指导意义。
1243 0