C详细讲解《自定义类型:结构体,枚举,联合》。

本文详细介绍了C语言中的结构体、枚举和联合的使用。结构体包括类型声明、自引用、内存对齐及传参等;枚举涉及定义、优点和应用;联合则讲解了其定义、特点和大小计算。文中还讨论了位段的内存分配和跨平台问题,并通过实例展示了如何利用结构体和联合进行内存操作。同时,文章对比了枚举和#define定义常量的区别,强调了枚举在代码可读性和可维护性方面的优势。
摘要由CSDN通过智能技术生成
自定义类型:结构体,枚举,联合。
本节重点
结构体(struct)
◻结构体类型声明
◻结构体的自引用
◻结构体变量的定义和初始化
◻结构体内存对齐
◻结构体传参
◻结构体实现位段(位段的填充&可移植性)
枚举(enum)
◻枚举的定义
◻枚举的优点
◻枚举的使用
联合(union)
◻联合的定义
◻联合的特点
◻联合大小的计算
1.结构体的基础知识
1.1结构体是一些值的集合,这些值称为成员变量,结构体的每个成员可以是不同类型的变量.
1.2结构体的声名
struct tag
{
    memder-list;//1.成员列表
}variadle-list;//2.变量列表

​ 举例说明如下对应

struct Stu
{//这相当于成员列表
    char name[20];
    int age;
    double score;
}s1,s2,s3;//这相当于变量列表

struct Stu s1;//当然也可以声名全局的

int main()
{
    struct Stu s2,s3;//这是声明局部的
}

1.3特殊声明

在声明结构体的时候,可以不完全的声明

比如:

//匿名结构体    意味只用一次
 struct
{
	int a;
    char b;
    float c;
}x;//必须加声明列表; 
	//注:匿名结构体的成员一样,在编译器看来也是不同类型的结构体类型
 struct
 {
     int a;
     char b;
     float c;
 }a[20],*p;
 
int main()
{
    p=&x;//这是错误的虽成员一样,但在编译器看来也是不同类型的结构体类型
    return 0;
}

上面两个结构体在声明的时候掉了结构体标签 (tag或Stu)

1.4结构体的自引用
typedef struct Node//typedef类型重命名把struct Node重命名为Node
{
    int date ;//数据
    struct Node* next;//指针
}Node;
int main()
{
    struct Node n1;//找到相同数据的类型
    Node n2;//重命明后n1和n2一样少了个struct Node
    return 0;
}
1.5结构体类型和初始化
struct Stu
{
    char name [20];//名字
    float num;//成绩
    char id[20];//评价
}s1={"王小明",66.6,"良"};//可以在这定义初始化
int main()
{
    struct Stu n={"张三",77.8,"优良"};//也可以在这定义初始化
    return 0;
}
1.6结构体类型对齐

规则如下

​ 1.第一个成员在与结构体变量为0地址处。

​ 2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

对齐数=编译器默认的一个对齐数(1.VS默认为8 2.Lin默认没有对齐数) 于该成员大小的较小值

​ 3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。

​ 4.如果嵌套了结构体的情况下,嵌套的结构对齐到自己的最大对齐数的整数倍数,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

struct S1
{
    char ci;//(为1的类型大小)第一个成员放到结构体变量为0地址处·
    int i;//(为4的类型大小)编译器默认大小与该成员大小的较小值为对齐数等于4
    char c2;//(为1的类型 大小)编译器默认大小与该成员大小的较小值为对齐数等于1
    //最后  求结构体最大对齐数与偏移量的整数倍对齐数为11
};
int main()
{
    struct S1 s;
    printf("%d\n",sizeof(struct S1));//字节大小为12
    return 0;
}

在这里插入图片描述

1.7修改默认对齐数

用**#pragma**这个预处理命令可以修改我们的默认对齐数。

#include<stdio.h>
#pragma pack(4)//修该默认对齐数为4
struct s1
{
    char c1;//1
    int c2;//4
    double c3;//4
};
int main()
{
    printf("%d\n",sizeof(struct s1));//字节大小为16
    return 0;
}

结论:

​ 结构在对齐方式不合适的时候我们可以修改默认对齐数

1.8结构体传参

struct S
{
    int dade[100];
    int num;
};
struct S s={{1,2,3,4},1000};
//结构体传参
void print1(struct S s)
{
    printf("%d\n",s.num);
}
//结构体地址传参
void print2(struct S* s)
{
    printf("%d\n",s->num);
}
int main()
{
    print1(s);//传结构体
    print2(s);//传地址
    return 0;
}

上面个的print1和print2那个函数好

答案是:print2好

原因:

函数传参的时候,参数都是压栈的,会有时间和空间上的系统开销。

如果传递一个结构对象的时候,结构体过大,参数压栈的系统开销过大,所以会导致性能下降。

结论:

结构体传参的时候,要传结构体的地址。

2.位段

2.1什么为位段

位段的声名和结构是类似的,有两个不同:

​ 1.位段的成员必须为int unsigned int 或sigend int.

​ 2.位段的成员名后面有一个冒号和数字。

比如:

struct A
{
    int _a:2;//_a这个成员只占2个bit位
    int _b:5;//_b这个成员只占5个bit位
    int _c:10;
    int _d:30;
};//用了8字节
int main()
{
    printf("%d\n",sizeof(struct A));
    return 0;
}

A就是一个位段类型

2.2位段的内存分配

1.位段的成员可以是int unsigned int singed int 或char(属于整形家族)类型。

2.位段的空间上是按照需要以4字节(int) 或1个字节(char)的方式来开辟的。

3.位段涉及很多不确定因素,位段是不跨平台的,注意可移植的程序应该避免使用位段。

//一个例子
struct S
{
    char a:3;
    char b:4;
    char c:5;
    char d:4;
};
int main()
{
    struct S s={0};//内存赋值为零
s.a=10;
s.b=12;
s.c=3;
s.d=4;
    return 0;
}

具体如下图

在这里插入图片描述

2.3位段的跨平台问题

​ 1.int 位段当成有符号类型还是无符号类型是不确定的。

​ 2.位段中最大的位数不能确定。(16位机器最大为16,32位机器最大为32,写成27,在16位机器中会出问题。)

​ 3.位段中的成员在内存中从左到右还是从右向左分配尚未定义。

​ 4.当一个结构包含两个位段,第二个位段成员比较大,无法容纳第一个位段剩余的位时,是舍弃剩余位还是利用,这是不确定的。

总结:

​ 跟结构相比,位段可以达到相同的效果,但是可以很好的节省空间,但是有跨平台的问题存在。

2.4位段的应用

在这里插入图片描述

3.枚举

枚举就是可以一一列举出来

如一周的星期一到星期日

还有男女

还有颜色

3.1枚举的定义

enum Sex
{
    MALE,//男
    FEMALE,//女
    SECRET
};
int main()
{
    printf("%d\n",MALE);//枚举是有确定值的为0
      printf("%d\n",FEMALE);//1
      printf("%d\n",SECRET);//2
    enum Sex s=MALE;//用枚举的可能取值定义好可以直接用
    enum Sex s2=FEMALE;
    return 0;
}

3.2枚举的优点

我们可以用#define定义常量,为什么还要用枚举?

​ 1.增加代码的可读性和可维护性

​ 2.和#define定义的标识符比较枚举有检查类型,更加严谨。

​ 3.防止了命名污染(封装)

​ 4.便于调试。

​ 5.使用方便,一次可以定义多个常量

4.联合(共用体)****

4.1联合类型的定义

联合也是一种特殊的自定义类型

这种类型定义的便量也包含一系列的成员,特征是这些成员公用一块空间,(所以也叫共用体)

比如:

union Un
{
    char c;
    int i;
};
int main()
{
    //联合变量的定义
    union Un un;
    //计算两个变量的大小
    printf("%d\n", sizeof(un));
    printf("%p\n", &un);
    printf("%p\n", &(un.c));
    printf("%p\n", &(un.i));

}

下图我们发现为4个字节且地址一样

所示他们公用了同一个内存且同一时间只用一个(所以也叫共用体)

****在这里插入图片描述**

4.2枚举的应用

int num()
{
    union Un
    {
        char c;
        int i;
    }u;
    u.i=1;
    return u.c;
}
int main()
{
    //判断大小端
    /*int i=1;
    if(1==*(char*)&i)
    {
        printf("小端");

    }else
        printf("大端");*/
    if(1==num())
    {
        printf("小端");

    }else
        printf("大端");
    
}
4.3联合体大小的计算
union Un
{
    char arr[5];对齐数1
    int i;对齐数4
};
int main()
{
     printf("%d\n", sizeof(un));//8
}
    int i;
}u;
u.i=1;
return u.c;

}
int main()
{
//判断大小端
/int i=1;
if(1==
(char*)&i)
{
printf(“小端”);

}else
    printf("大端");*/
if(1==num())
{
    printf("小端");

}else
    printf("大端");

}


##### 4.3**联合体大小的计算**

```c
union Un
{
    char arr[5];对齐数1
    int i;对齐数4
};
int main()
{
     printf("%d\n", sizeof(un));//8
}
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值