平地起C楼—第九层《详解构造数据类型》

在这里插入图片描述

🚩前言

在前面已经提到了C语言中各种的内置数据类型,比如:char\int\float\double\long long\long double等,这些都是C语言自身支持的数据类型。但是今天所说的是自定义数据类型。通过这些构造数据类型可以用来描述一个具体的对象。
构造数据类型:是一种可以包含多个不同的数据类型的数据组合,可以用来描述一个对象的属性。在C语言中构造类型有很多,比如:结构体、联合体(共用体)、枚举数据类型等。接下来就看看这些具体的构造数据类型——》👀👀

🥇结构体数据类型

我们从结构体定义开始道来:
在这里插入图片描述

🥈结构体的定义

  • 在使用结构体的时,必须对结构体的组成进行熟悉,其形式如下:
 struct 结构体名
{
   	结构体成员;
};
  • 结构体成员的定义和简单变量的定义形式一样。

1、比如我定义一个描述人的结构体

//结构体定义
struct Person
{
    char name[20];//姓名
    int age;//年龄
    char sex;//性别
    long indentityNo;//身份证号
    char addr[40];//地址
};

在上例子中,struct为关键字,Person为结构体名,大括号里面的就是成员。
在结构体类型中具有一些特点:
(1)结构体名为任何合法的标识符,建议命名的时候做到见名知意。
(2)虽然成员的定义和简单变量定义一样,但是不能直接使用。

🥈结构型变量的定义

一旦创建好结构体,就可以定义结构体变量了。我们可以采用3种方式定义结构体变量:

1、先定义结构体,再定义结构体变量
定义格式:类型标识符 <变量名列表>;
strcut Person stu,worker;

通过创建的Person结构体,创建了两个变量stuworker,两个都是结构体struct Person的变量。‘struct Person’代表类型名,即表示它是结构体类型。如同int定义两个变量abint a,b),int就是类型标识符,表示ab是整型。
格式不要错写成:
struct stu,worker;(没有声明是哪一种的结构体类型)
Person stu,worker;(没有关键字struct,就不认为是结构体)。
这两种都是错误的格式。
在定义好结构体类型后就可以定义不同变量了:
struct Person teacher,doctor;
struct Person *student;

2、在定义结构体的同时定义一个或者多个结构体类型变量
定义格式:

struct <结构体名>
{
	成员列表;
}<变量名1>……;
struct Person
{
    long no;
    char name[20];
    int age;
    char sex;
    long indentityNo;
    char addr[40];
}student,doctor,worker;

struct Person stu;//在外面有定义变量

在此定义的基础上还可以在外面定义结构体变量。

3、直接定义结构体变量名——《匿名结构体》
定义格式:

struct 
{
	成员列表;
}<变量名>;
struct 
{
    long no;
    char name[20];
    int age;
    char sex;
    long indentityNo;
    char addr[40];
}student,doctor,worker;

//struct  stu;//不合法,因为没有定义结构体名

匿名结构体定义方法:就是只定义了3个变量student,doctor,worker,但没有定义结构体名,因此就不能再定义其他变量了。

当然,在定义变量的时候,既可以是全局变量,也可以是局部变量。

🥈结构体内存对齐

在清楚结构体怎么创建后,来看看结构体的内存对齐问题,也就是讨论结构体在内存中的字节大小。我们先通过例子来打开内存对齐的知识点:

//结构体内存对齐
#include<stdio.h>
struct S
{
    char c1;//1Byte
    int num;//4Byte
    char c2;//1Byte
}s;
int main()
{
    printf("%zd\n",sizeof(s));//6Byte?为什么输出12Byte
    return 0;
}

为什么是12Byte?
在这里插入图片描述
内存对齐规则:

  1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处。也就是第一个成员必须是结构体首地址处。
  2. 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
    对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。
  • VS 中默认的值为 8
  • Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩
  1. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的整数倍。
  2. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。

图解为什么是12Byte?
在这里插入图片描述

我们看到确实是12Byte,但是浪费了6Byte,接下来换一个顺序来看一看:

//2、结构体内存对齐
#include<stdio.h>
struct S
{
    char c1;//1Byte
    char c2;//1Byte  
    int num;//4Byte
}s;
int main()
{
    printf("%zd\n",sizeof(s));
    return 0;
}
  • 结果展示:
    在这里插入图片描述

图解:
在这里插入图片描述

注意:内存对齐,不能理解为,每个成员所占空间要和成员中占字节最大的成员一样。也就是两个char类型成员和一个int 类型成员,在算总字节时候不能把char类型的字节算作是4Byte,如果这样算总字节就是3*4=12,这样是错误的理解。必须根据偏移量来算。

🥈结构型变量的初始化

在了解了结构体的创建后,接下来就得给成员变量初始化了,结构体成员初始化,和其他简单变量初始化一样,结构体变量也可以在变量定义的时候进行初始化。比如下面代码中就是在定义变量的同时加以初始化:

//3、结构体成员初始化
#include<stdio.h>
struct Person
{
    char name[15];//不能直接在成员变量后面初始化,char name[15]="张三";
    //因为结构体规定结构体成员变量不能直接使用。
    int age;
    char sex;
    char addr[40];
}student={"轩辕",25,'男',"重庆"};

如果结构体嵌套结构体,初始化的时候需要对各个成员初始化:

//4、结构体嵌套
#include<stdio.h>
struct Data
{
    int day;
    int month;
    int year;
};
struct Person
{
    char name[15];
    struct Data birthday;
    int age;
    char sex;
    char addr[40];
}student={"张三",23,5,2014,10,'男',"重庆"};

当我们定义的是结构体指针变量的时候,不能直接初始化指针变量。就像下面这种写法是错误的:
struct Person *worker={"王岑",26,"软件工程","上海"};//错误的初始化。
struct Person *worker = &((struct Person){"王岑", 26, "软件工程", "上海"});//结构体指针变量正确初始化

也可以在外面初始化

#include<stdio.h>
struct Person
{
    char name[15];
    int age;
    char sex;
    char addr[40];
};

struct Person student1={"轩辕",25,'男',"重庆"};//全局变量

int main()
{
    struct Person student1={"轩辕",25,'男',"重庆"};//局部变量
    return 0;
}

🥈结构型变量成员的引用

结构体创建好了,成员变量也初始化好了。接下来我们来看看该怎么访问初始化好的成员呢?

🥉引用结构体类型变量中的成员

格式:
<结构体变量名>.<成员名>
或者 <(*结构体指针变量名)>.<成员名>
或者<结构体指针变量名> -><成员名>

//结构体解引用
#include<stdio.h>
//创建结构体
struct Person
{
    char name[15];
    int age;
    char profession[30];
    char addr[50];
};

int main()
{
    //创建结构体变量
    struct Person student1={"张三",21,"计算机","重庆"};
    struct Person *worker=&(struct Person){"王岑",26,"软件工程","上海"};
    //此处不能写为struct Person *worker={"王岑",26,"软件工程","上海"};
    //因为是指针变量不能直接初始化。
    
    struct Person *teacher=&((struct Person){"文宇",29,"计算机老师","成都"});

    printf("姓名\t年龄\t专业\t\t地址\n");
	//解引用操作来访问
    printf("%s\t%d\t%s\t\t%s\n",student1.name,student1.age,
    student1.profession,student1.addr); 
    printf("%s\t%d\t%s\t%s\n",(*worker).name,(*worker).age,
    (*worker).profession,(*worker).addr); 
    printf("%s\t%d\t%s\t%s\n",teacher->name,teacher->age,
    teacher->profession,teacher->addr);
    return 0;
}

结果展示:
在这里插入图片描述

🥉将结构体变量作为一个整体来使用

可以将一个结构体变量作为一个整体赋值给另一个结构体变量,前提是两个结构体变量必须具有相同的结构体类型。
比如:
struct Person doctor={"Li_ming",25,"高数老师","贵州"};
struct Person teacher=doctor;
teacher=doctor;
//赋值以后teacher中的信息和doctor中的信息相同的。

  1. 以下是两个结构体——课程成绩和学生信息。看结构体成员的使用。
#include<stdio.h>
//学生成绩信息结构体
struct score
{
    int math;
    int english;
    int computer;
};

//学生基本信息结构体
struct stu
{
    char name[15];
    char sex;
    long stuNo;
    struct score sub;
};

int main()
{
    struct stu student1={"张三",'M',20230001,85,76,95};
    struct stu student2;
    student2=student1;
    
    printf("姓名\t性别\t学号\t\t高数\t英语\t计算机\n");
    printf("%s\t%c\t%d\t%d\t%d\t%d\n",
    student1.name,student1.sex, student1.stuNo,student1.sub.math,
    student1.sub.english,student1.sub.computer);

    printf("%s\t%c\t%d\t%d\t%d\t%d\n",
    student2.name,student2.sex, student2.stuNo,student2.sub.math,
    student2.sub.english,student2.sub.computer);
    return 0;
}

结果展示:可以看到student1的信息给到了student2,打印出来是一样的。
这里是引用

🥇结构体数组

简单来说,一个结构体变量只能存放一个对象的数据,上述代码中就是创建student1student2便是两个学生信息,但是要存储50个学生呢?难道创建50个学生对象吗?不会觉得很难写吗?理论上是可以的,只是很麻烦。在这就会用到结构体数组,即数组中的每个元素都是结构体类型变量。我们来看看吧。

🥈结构体数组的定义

可以用以下3种方式定义:

1、先定义结构体,再定义结构体数组

struct <结构体名>
{
	成员列表;
};
struct <结构体名>  <数组名>[数组大小];

2、在定义结构体的同时定义结构体数组

struct <结构体名>
{
	成员列表;
}<数组名>[数组大小];

3、匿名结构体定义结构体数组

struct 
{
	成员列表;	
}<数组名>[数组大小];

🥈结构体数组成员的初始化和引用

直接用代码演示:

//7、结构体数组初始化
#include<stdio.h>
struct Person
{
    char name[10];
    int age;
    char sex;
    char addr[20];
};

int main()
{
    struct Person student[30]={{"hang Ming",23,'F',"潮汕"},
    {"Zhao Chen",25,'M',"成都"},
    {"Zhou Ting",26,'F',"昆明"}};
    //student[3]~student[29]是不确定的。

    printf("姓名\t\t年龄\t性别\t地址\n");
    for(int i=0;i<3;i++)
    {
        printf("%2s\t%2d\t%2c\t%2s\n",student[i].name,student[i].age,
        student[i].sex,student[i].addr);
    }
    
    return 0;
}

结果展示:
在这里插入图片描述

🥇结构体类型变量与函数

利用函数来对结构体变量进行操作:

🥈函数的形参和实参为结构体

  • 设计一个函数来打印处购书的信息:代码如下——》
//8、结构体变量与函数
#include<stdio.h>
//购书信息结构体
struct BookList
{
    char name[20];
    int number;
    float price;
    float SumMoney;
};

//输出函数
void print_book_list(struct BookList book)//形参接收
{
    book.SumMoney=book.number*book.price;
    printf("%-24s%d\t%.2f\t%.2f\n",book.name,book.number,
    book.price,book.SumMoney);
}

int main()
{
    //购置的书数量不止一本,所以用结构体数组
    struct BookList Book[4];
    float Total=0;
    printf("请输入这4本书的信息:书名  数量  单价\n");
    for(int i=0;i<4;i++)
    {
        scanf("%s%d%f",&Book[i].name,&Book[i].number,&Book[i].price);
        Total+=Book[i].number*Book[i].price;
    }
    printf("--------------------------------------------\n");
    printf("购置书清单:\n");
    printf("书名\t\t\t数量\t单价\t合计\n");
    for(int i=0;i<4;i++)
    {
        print_book_list(Book[i]);//传过去实参
    }
    printf("购书金额总计:%.2f\n",Total);
    return 0;
}

结果展示:
在这里插入图片描述

🥈函数返回值为结构体类型

我们知道int,float,double,void,各种数据的指针类型都可以作为返回值。在新的C语言标准中函数返回值可以是结构体相关的类型:
代码如下:

//1、结构体数组作为函数返回值的使用
#include<stdio.h>
//创建购置书的信息结构体
struct BookList
{
    char name[30];
    int number;
    float price;//单价
    float SumMoney;//总计
};
//两个函数
//1、struct BookList* InputBookInfo(struct BookList *book);//返回的是结构体指针类型
//2、void Print_Book_List();//打印函数
float Total=0;//全局变量存储总计金额

//用结构体指针类型作为返回值,传入的是地址,参数用指针
struct BooKList* InputBookInfo( struct BookList *book)
{
   
     scanf("%s%d%f",&book->name,&book->number,&book->price);
     Total+=book->number*book->price;
    return book;
} 

void Print_Book_List(struct BookList book)
{
    book.SumMoney=book.number*book.price;
    printf("%-24s%d\t%.2f\t%.2f\n",book.name,book.number,book.price,book.SumMoney);
}
int main()
{
    //创建结构体数组变量
   struct BookList Book[4];
    //输入信息
    printf("请输入购置书的信息:书名  数量  单价\n");
    for(int i=0;i<4;i++)
    {
        InputBookInfo(&(Book[i]));//调用购置书的信息输入函数
    }
    printf("----------------------------------------\n");
    printf("购书清单:\n");
    printf("书名\t\t\t数量\t单价\t合计\n");
    for(int i=0;i<4;i++)
    {
        Print_Book_List(Book[i]);
    }
    printf("购书金额总计:%.2f\n",Total);

}

结果展示:
在这里插入图片描述

🥇共用体数据类型

共用体,也被称为联合体。它是把不类型的数据项组合成一体的,这些数据项在内存单元中所占用的起始单元是相同的。
它的定义和结构体一样,只是关键字不一样:👀👀

🥈共用体的定义

1、先定义共用体,再定义共用体类型

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

2、在定义共用体的同时定义共用体变量

union <共用体名>
{
	成员列表;
}<变量名1>……;//可以多个变量名

3、匿名共用体

union 
{
	成员列表;
}<变量名1>……;//可以多个,但是不能再定义另外的共用体变量。

🥈共用体与结构体的内存存储对比

//2、共用体和结构体内存
#include<stdio.h>
//创建结构体
struct memb
{
    float f;
    int i;
    char c;
};

//创建共用体
union Un
{
    float f1;
    int n;
    char c;
};

int main()
{
    //创建变量
    struct memb st={0};
    union Un un={0};
    //求所占字节大小
    printf("结构体:%zd\n",sizeof(st));//12Byte
    
    printf("共用体:%zd\n",sizeof(un));//4Byte
    return 0;
}

结果展示:
在这里插入图片描述

在这里插入图片描述

🥈共用体的初始化与解引用

共用体类型的变量的引用方式和结构体变量引用方式类似:

<共用体名>.<成员名>;
  • 通过代码了解共用体成员初始化:
//3、共用体成员的初始化
#include<stdio.h>
//创建共用体
union Un
{
    double de;
    int num;
    char c;
};

int main()
{
    //创建共用体变量
    union Un tag;
    tag.de=6.66;
    tag.num=20;
    tag.c='p';
    printf("共用体成员的值:\n");
    printf("tag.de=%lf\ntag.num=%d\ntag.c=%c\n",tag.de,tag.num,tag.c);
    return 0;
}

结果展示:和赋给的值有点差别
在这里插入图片描述

🥇枚举数据类型

枚举类型,是用标识符表示的整型常量的集合,从其作用上看,枚举型常量是自动设置值的符号常量。定义形式如下:
枚举型常量的起始值为0

enum <枚举类型名>
{
	标识符1,
	标识符2,
	……
};

代码展示:

//4、枚举的使用
#include<stdio.h>
//创建枚举
enum Months
{
    //January,
    January=1,
    February,
    March,
    April,
    May,
    June,
    July,
    August,
    September,
    October,
    November,
    December
};
int main()
{
    //只有在定义枚举类型后,在可以创建枚举变量
    enum Months month;//创建枚举变量
    for(month=January;month<=December;month++)
    {
        printf("%2d  ",month);
    }
    return 0;
}

第一个结果是January赋值为1的结果,默认是从起始位置0开始:
第二个是把January赋值为1的,就从1开始。
在这里插入图片描述

等待下一节知识吧👀
在这里插入图片描述

  • 25
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值