结构体、共用体、枚举

一、结构体的概念

在程序开发的时候,有些时候我们需要将不同类型的数据组合成一个有机的整体,
以便于引用。如: 一个学生有学号/姓名/性别/年龄/地址等属性
 

int num;
char name[20]
char sex;
int age;
char addr[30];


显然单独定义以上变量比较繁琐,数据不便于管理,所以在 C 语言中就发明了结构体类型。

结构体是一种构造数据类型。

前面学过一种构造类型——数组:
构造类型:
        不是基本类型的数据结构也不是指针类型,它是若干个相同或不同类型的数据构成的集合

描述一组具有相同类型数据的有序集合,用于处理大量相同类型的数据运算--数组 结构体类型的概念:
        结构体是一种构造类型的数据结构,是一种或多种基本类型或构造类型的数据的集合。

二、结构体类型定义

结构体类型的定义方法
咱们在使用结构体之前必须先有类型,然后用类型定义数据结构 这个类型相当于一个模具

(1).先定义结构体类型,再去定义结构体变量


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

例1:

struct stu{

int num;

char name[20];

char sex;

};

//有了结构体类型后,就可以用类型定义变量了

struct stu   lucy,bob,lilei;   //定义了三个 struct stu 类型的变量

每个变量都有三个成员,分别是 num name   sex

咱们可以暂时认为结构体变量的大小是它所有成员之和

(2).在定义结构体类型的时候顺便定义结构体变量,以后还可以定义结构体变量

struct 结构体类型名{

成员列表;

}结构体变量 1,变量 2;

struct 结构体类型名   变量 3,变量 4;

例2:

struct stu{

int num ;

char name[20] ;

char sex ;

}lucy,bob,lilei;

struct stu xiaohong,xiaoming;

(3).在定义结构体类型的时候,没有结构体类型名,顺便定义结构体变量,因为没有类型名,所以以后不能再定义相关类型的数据了

struct    {

成员列表;

}变量 1,变量 2;

例3:

struct{

int num;

char name[20];

cahr sex ;

}lucy,bob;

以后没法再定义这个结构体类型的数据了,因为没有类型名

(4).最常用的方法
        通常咱们将一个结构体类型重新起个类型名,用新的类型名替代原先的类型

步骤 1:先用结构体类型定义变量
        struct stu{
        int num;
        char name[20];
        char sex;
        }bob;
步骤 2:新的类型名替代变量名

struct stu{
int num;
char name[20];
char sex;
}STU;

步骤 3:在最前面加 typedef
typedef struct stu{
int num;
char name[20];
char sex;
}STU;

注意:步骤 1 和步骤 2,在草稿上做的,步骤 3 是程序中咱们想要的代码

以后 STU 就相当于 struct stu
STU lucy;和 struct stu lucy;是等价的。]

三、结构体变量的定义初始化及使用

1、结构体变量的定义和初始化
        结构体变量,是个变量,这个变量是若干个相同或不同数据构成的集合

注:
(1):在定义结构体变量之前首先得有结构体类型,然后再定义变量
(2):在定义结构体变量的时候,可以顺便给结构体变量赋初值,被称为结构体的初始化 (3):结构体变量初始化的时候,各个成员顺序初始化。

例4:

struct stu {

int num ;

char name[20];

char sex ;

};

struct stu boy ;

struct stu lucy{

101,

"lucy" ,

'f'

} ;

2、结构体变量的使用

定义了结构体变量后,要使用变量

(1).结构体变量成员的引用方法

结构体变量.成员名

例 5:

结构体、共用体、枚举_第1张图片

例6:

结构体、共用体、枚举_第2张图片

(2).结构体成员多级引用
   例 7:

结构体、共用体、枚举_第3张图片

3相同类型的结构体变量可以相互赋值
        注意:必须是相同类型的结构体变量,才能相互赋值。

例8:

#include

struct stu{ 
    int num; 
    char name[20]; 
    char sex; 
}; 
int main(int argc, char *argv[]) 

    struct stu bob={101,"bob",'m'};
struct stu lilei;

lilei=bob;
printf("%d %s %c\n",lilei.num,lilei.name,lilei.sex); 
    return 0; 
}

四、结构体数组

结构体数组是个数组,由若干个相同类型的结构体变量构成的集合

1、结构体数组的定义方法
        struct 结构体类型名 数组名[元素个数];

例9

#include

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

struct stu edu[3];//定义了一个 struct stu 类型的结构体数组 edu,这个数组有 3 个元素分别是 edu[0]edu[1]edu[2]

1、结构体数组元素的引用 数组名[下标]

2、数组元素的使用
edu[0].num =101;// 101 edu 数组的第 0 个结构体变量的 num 赋值 strcpy(edu[1].name,"lucy");

例10:

结构体、共用体、枚举_第4张图片

五、结构体指针

即结构体的地址,结构体变量存放内存中,也有起始地址
咱们定义一个变量来存放这个地址,那这个变量就是结构体指针变量。

结构体指针变量也是个指针,既然是指针在 32 位环境下,指针变量的占 4 个字节,存放一个地址编号。

1、结构体指针变量的定义方法:struct 结构体类型名   * 结构体指针变量名;

struct stu{
int num;
char name[20];
};

struct stu * p;  //定义了一个 struct stu *类型的指针变量

变量名 是 p,p 4 个字节,用来保存结构体变量的地址编号

struct stu boy;
p=&boy;
访问结构体变量的成员方法:

例11:

boy.num=101; //可以,通过 结构体变量名.成员名

(*p).num=101; //可以,*p 相当于 p 指向的变量 boy

p->num=101;  //可以,指针->成员名

通过结构体指针来引用指针指向的结构体的成员,前提是指针必须先指向一个结构体变量。

结构体指针应用场景:
        (1):保存结构体变量的地址
        例 12:

结构体、共用体、枚举_第5张图片

 

(2):传结构体变量的地址
13

结构体、共用体、枚举_第6张图片

(3):传结构体数组的地址

结构体数组,是由若干个相同类型的结构体变量构成的集合。存放在内存里,也有起始地址,其实就是第 0 个结构体变量的地址。

例14:

结构体、共用体、枚举_第7张图片

注意:(1)结构体变量的地址编号和结构体第一个成员的地址编号相同,但指针的类型不同

例15:

结构体、共用体、枚举_第8张图片

(2):结构体数组的地址就是结构体数组中第 0 个元素的地址

例16:

结构体、共用体、枚举_第9张图片

六、结构体内存分配

1、结构体内存分配

结构体变量大小是,它所有成员的大小之和。

因为结构体变量是所有成员的集合。

例17:

结构体、共用体、枚举_第10张图片

但是在实际给结构体变量分配内存的时候,是规则的.

例18:

结构体、共用体、枚举_第11张图片

规则 1:以多少个字节为单位开辟内存

给结构体变量分配内存的时候,会去结构体变量中找基本类型的成员,哪个基本类型的成员占字节数多,就以它大大小为单位开辟内存,

gcc 中出现了 double 类型的,例外

(1):成员中只有 char 型数据 ,以 1 字节为单位开辟内存。

(2):成员中出现了 short int 类型数据,没有更大字节数的基本类型数据。

2 字节为单位开辟内存

(3):出现了 int float 没有更大字节的基本类型数据的时候以 4 字节为单位开辟内存。

(4):出现了 double 类型的数据

情况 1:在 vc6.0 Visual Studio 中里,以 8 字节为单位开辟内存。

情况 2 Linux 环境 gcc 里,以 4 字节为单位开辟内存。

无论是那种环境,double 型变量,占 8 字节。

注意:

如果在结构体中出现了数组,数组可以看成多个变量的集合。

如果出现指针的话,没有占字节数更大的类型的,以 4 字节为单位开辟内存。

在内存中存储结构体成员的时候,按定义的结构体成员的顺序存储。

例19:

struct stu{
char sex;
int age;
}lucy;
lucy 的大小是 4 的倍数。

规则 2:字节对齐
        (1)char 1 字节对齐 ,即存放 char 型的变量,内存单元的编号是 1 的倍数即可。

        (2)short int 2 字节对齐 ,即存放 short int 型的变量,起始内存单元的编号是 2 的倍数即可
        (3)int 4 字节对齐 ,即存放 int 型的变量,起始内存单元的编号是 4 的倍数即可
        (4)long int 32 位平台下,4 字节对齐 ,即存放 long int 型的变量,起始内存单元的编号是 4 的倍数即可
        (5)float 4 字节对齐 ,即存放 float 型的变量,起始内存单元的编号是 4 的倍数即可
        (6)double
        a.vc6.0 Visual Studio 环境下
        8 字节对齐,即存放 double 型变量的起始地址,必须是 8 的倍数,double 变量占 8 字节         b.gcc 环境下
        4 字节对齐,即存放 double 型变量的起始地址,必须是 4 的倍数,double 变量占 8 字节。

注意 3:当结构体成员中出现数组的时候,可以看成多个变量。

注意 4:开辟内存的时候,从上向下依次按成员在结构体中的位置顺序开辟空间。

例20://temp8个字节

结构体、共用体、枚举_第12张图片

结果分析:
a 的地址和 b 的地址差 2 个字节
b 的地址和 c 的地址差 2 个字节

例 21:temp 的大小为 12 个字节

结构体、共用体、枚举_第13张图片

 

结果分析:
        a c 的地址差 4 个字节
        c b 的地址差 4 个字节

例22:

struct stu{
char buf[10];
int a;
}temp;

//temp 占 16 个字节

例23:

在 vc 和 Visual Studio 中占    16 个字节 a 和 b 的地址差 8 个字节
在 gcc 中占字节a 和 b 的地址差 4 个字节

结构体、共用体、枚举_第14张图片

为什么要有字节对齐?

用空间来换时间,提高 cpu 读取数据的效率

struct stu{

char a;

int b;

}boy

结构体、共用体、枚举_第15张图片       结构体、共用体、枚举_第16张图片

存储方式 1                                       存储方式 2

指定对齐原则:使用#pragma pack改变默认对齐原则

格式:#pragma pack (value)时的指定对齐值value

注意:1.value只能是:1 2 4 8

2.指定对齐值与数据类型对齐值相比取较小值

说明:咱们指定一个value

(1):以多少个字节为单位开辟内存

结构体成员中,占字节数最大的类型长度和value比较,取较小值,为单位开辟内存

例24:

结构体、共用体、枚举_第17张图片

例25:

结构体、共用体、枚举_第18张图片

(2):字节对齐
结构体成员中成员的对齐方法,各个默认的对齐字节数和value相比,取较小值

例26:

结构体、共用体、枚举_第19张图片

例27:

结构体、共用体、枚举_第20张图片

ab都按原先的对齐方式存储

如:如果指定对齐值:

设为1:则shortintfloat等均为1

设为 2:则 char 仍为 1short 2int 变为 2

七、位段

一、位段

在结构体中,以位为单位的成员,咱们称之为位段(位域)

struct stu{

unsigned int a:2;

unsigned int b:6;

unsigned int c:4;

unsigned int d:4;

unsigned int i;

} data;

注意:不能对位段成员取地址

例28:

结构体、共用体、枚举_第21张图片

位段注意:

1、对于位段成员的引用如下:

data.a =2

赋值时,不要超出位段定义的范围;

如段成员a定义为2位,最大值为3,即(112

所以data.a =5,就会取5的低两位进行赋值 101

2、位段成员的类型必须指定为整型或字符型

3、一个位段必须存放在一个存储单元中,不能跨两个单元

第一个单元空间不能容纳下一个位段,则该空间不用,

而从下一个单元起存放该位段

位段的存储单元:

(1)char 型位段 存储单元是 1 个字节

(2)short int 型的位段存储单元是 2 个字节

(3)int 的位段,存储单元是 4 字节

(4)long int 的位段,存储单元是 4 字节

struct stu{

char a:7;

char b:7;

char c:2;

}temp;// 3 字节,b 不能跨 存储单元存储

例29:

结构体、共用体、枚举_第22张图片

结果为:3 ,证明位段不能跨其存储单元存储

注意:不能 取 temp.b 的地址,因为 b 可能不够 1 字节,不能取地址。

4、位段的长度不能大于存储单元的长度

(1)char 型位段不能大于 8

(2)short int 型位段不能大于 16

(3)int 的位段,位段不能大于 32

(4)long int 的位段,位段不能大于 32

例30:

结构体、共用体、枚举_第23张图片

分析:编译出错,位段 a 不能大于其存储单元的大小

5、如一个段要从另一个存储单元开始,可以定义:

unsigned    char a:1;

unsigned    char b:2;

unsigned    char :0;

unsigned    char c:3;(另一个单元)

由于用了长度为 0 的位段,其作用是使下一个位段从下一个存储单元开始存放将 ab 存储在一个存储单元中,c 另存在下一个单元

例31:

结构体、共用体、枚举_第24张图片

6、可以定义无意义位段,如:

unsigned a: 1;

unsigned : 2;

unsigned b: 3;

八、共用体

1:共用体和结构体类似,也是一种构造类型的数据结构。

既然是构造类型的,咱们得先定义出类型,然后用类型定义变量。

定义共用体类型的方法和结构体非常相似,把 struct 改成 union 就可以了。

在进行某些算法的时候,需要使几种不同类型的变量存到同一段内存单元中,几个变量所使用空

间相互重叠。

这种几个不同的变量共同占用一段内存的结构,在C语言中,被称作“共用体”类型结构

共用体所有成员占有同一段地址空间

共用体的大小是其占内存长度最大的成员的大小

例33:

结构体、共用体、枚举_第25张图片

例34:

结构体、共用体、枚举_第26张图片

结果:temp2 的大小为 4 个字节,下面几个地址都是相同的,证明了共用体的各个成员占用同一块内存。

共用体的特点:
        1、同一内存段可以用来存放几种不同类型的成员,但每一瞬时只有一种起作用
        2、共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖
        3、共用体变量的地址和它的各成员的地址都是同一地址
        4、共用体变量的初始化
union data a={123}; 初始化共用体只能为第一个成员赋值,不能给所有成员都赋初值

例35:

结构体、共用体、枚举_第27张图片

九、枚举

将变量的值一一列举出来,变量的值只限于列举出来的值的范围内

枚举类型也是个构造类型的,类型定义类似结构体类型的定义

使用枚举的时候,得先定义枚举类型,再定义枚举变量

1、枚举类型的定义方法
      enum 枚举类型名{
      枚举值列表;
      };

      在枚举值表中应列出所有可用值,也称为枚举元素
枚举元素是常量,默认是从 0 开始编号的。

枚举变量仅能取枚举值所列元素

2、枚举变量的定义方法
enum 枚举类型名      枚举变量名;

例37:

结构体、共用体、枚举_第28张图片

① 枚举值是常量,不能在程序中用赋值语句再对它赋值 例如:sun=5; mon=2; sun=mon; 都是错误的.

② 枚举元素本身由系统定义了一个表示序号的数值
    默认是从0开始顺序定义为012
    如在week中,mon值为0tue值为1, …,sun值为6
③ 可以改变枚举值的默认值:如
enum week //枚举类型
{
mon=3tuewedthufri=4sat,sun
};
mon=3 tue=4,以此类推
fri=4 以此类推

注意:在定义枚举类型的时候枚举元素可以用等号给它赋值,用来代表元素从几开始编号

在程序中,不能再次对枚举元素赋值,因为枚举元素是常量。

你可能感兴趣的:(c语言,学习,算法)