在我们学习C语言的时候,有几个自定义类型需要我们掌握,那么我也是根据自己的学习来分享一下自己的理解。希望与大家互相学习,共同进步!

文章目录

  • 一:结构体
    • 1.1:结构体的基础知识
    • 1.2:结构的声明
    • 1.3:特殊的声明
    • 1.4:结构体的自引用
    • 1.5:结构体变量的定义和初始化
    • 1.6: 结构体内存对齐
    • 1.7:修改默认对齐数
    • 1.8:结构体传参
  • 二:位段
    • 2.1:什么是位段
    • 2.2:位段的内存分配
    • 2.3:位段的跨平台问题
  • 三:枚举
    • 3.1:枚举类型的定义
    • 3.2:枚举的优点
    • 3.3:枚举的使用
  • 四:联合(共用体)
    • 4.1联合类型的定义
    • 4.2 联合的特点
    • 4.3:联合大小的计算

一:结构体

1.1:结构体的基础知识

结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。

1.2:结构的声明

struct tag
{member-list;
}variable-list;//全局变量

例如描述一个学生:

struct Stu
{char name[20];//mingziint age;//nianlinchar sex[2];//xingbiechar id[20];//xuehao

1.3:特殊的声明

在声明结构体的时候。可以不完全的声明。
比如匿名结构体类型。

struct
{int a;char b;float c;
}x;struct
{int a;char b;float c;
}a[20], *p;

上面的两个结构体在声明的时候都省略掉了结构体标签(tag)。

那么问题来了?
p = &x; 合法吗?

警告:编译器会把上面两个声明当成完全不同的两个类型,所以是非法的。

1.4:结构体的自引用

在结构体中包含一个类型为该结构体本身的成员是否可以呢?

struct Node
{int data;struct Node next;
};

这种自引用是不可行的!
下面是正确的自引用方式:

struct Node
{int data;struct Node * next;//结构体的指针类型
};

1.5:结构体变量的定义和初始化

struct Point
{int x;int y;
}p1;//声明类型的同时定义全局变量p1;struct Point p2;//定义结构体变量p2;//初始化:定义变量的同时赋初值、
struct Point p3 = {x, y};

1.6: 结构体内存对齐

我们已经掌握了结构体的基本使用,那么我们现在来讨论结构体的大小

int main()
{struct s1{char c1;int i;char c2;};printf("%d\n", sizeof(struct s1));//12struct s2{char c1;char c2;int i;};printf("%d\n", sizeof(struct s2));//8struct s3{double d;char c;int i;};printf("%d\n", sizeof(struct s3));//16struct s4{char c1;struct s3 S3;int i;};printf("%d\n", sizeof(struct s4));//32system("pause");return 0;
}

如何计算结构体大小?
首先得掌握结构体的对齐规则:

1:第一个成员在与结构体变量偏移量为0的地址处。
2:其他成员变量要对齐到对齐数的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数与该成员大小的较小值。(VS中默认的值为8)
3:结构体的总大小为最大对齐数的整数倍。
4:如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(包括嵌套结构体的对齐数)的整数倍

为什么要存在内存对齐?
总的来说是:结构体的内存对齐是拿空间换取时间的做法!
那么在设计结构体的时候,我们既要满足对齐,又要节省空间**,那么就让占用空间小的成员尽量集中在一起。**

1.7:修改默认对齐数

之前我们见过了#pragma这个预处理指令,我们再次使用,可以改变我们的默认对齐数。

#pragma pack(8)
struct s1
{char c1;int i;char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认!#pragma pack(1)//设置默认对齐数为1
struct s2
{char c1;int i;char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认!int main()
{//输出的结果是什么printf("%d\n", sizeof(struct s1));//12printf("%d\n", sizeof(struct s2));//6system("pause");return 0;
}

结论:结构在对齐方式不合适的时候,我们可以自己更改默认对齐数。

1.8:结构体传参

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

上面的print1 和print 2 函数哪个好些?
答案是:首选print2 函数

原因:函数传参的时候,参数是需要压栈的,会有时间和空间上的系统开销。如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销较大,导致性能的下降。

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

二:位段

2.1:什么是位段

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

  1. 位段的成员必须是int,unsigned int , 或 signed int, short。
  2. 位段的成员名后边有一个冒号和一个数字

比如:

struct A
{int a : 2;int b : 5;int c : 10;int d : 30;
};
int main()
{printf("%d\n", sizeof(struct A));//8system("pause");return 0;
}

A就是一个位段类型,那位段的大小是多少?
理解:位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟,成员后面的数字代表所需要的bit位大小

2.2:位段的内存分配

  1. 位段的成员可以是int,unsigned int,signed int,或者 char(属于整型家族)类型。
  2. 位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟。
  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
//一个例子
struct S
{char a:3;char b:4;char c:5;char d:4;
};
struct S s = {0};
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;

空间是如何开辟的?

2.3:位段的跨平台问题

  1. int 位段被当成有符号数还是无符号数是不确定的。
  2. 位段中最大位的数目是不能确定的。(16位机器最大16,32位机器最大32)。
  3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
  4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。

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

三:枚举

枚举顾名思义就是举例,把可能的取值列举。

3.1:枚举类型的定义

enum Day
{Mon,Tues,Wed,Thur,Fri,Sat,Sun
};enum Sex
{MALE,FEMALE,SECRET
};

以上定义的enum Day,enum Sex,都是枚举类型。{}中的内容是枚举类型的可能取值,也叫枚举常量。这些默认取值是有值的。默认从0开始,依次递增1,当然在定义的时候也可以赋初值。
例如:

enum Color
{red = 1,Green = 2,Blue = 4
};

3.2:枚举的优点

我们可以使用#define定义常量,为什么非要使用枚举?
枚举的优点

  1. 增加代码的可读性和可维护性。
  2. 和#define定义的标识符比较,枚举有类型检查,更加严谨。
  3. 防止了命名污染(封装)。
  4. 便于调试。
  5. 使用方便,一次可以定义多个常量。

3.3:枚举的使用

enum Color
{Red = 1,Green = 2,Blue = 4
};int main()
{enum Color clr = Green;system("pause");return 0;
}

注意:只能使用枚举常量给枚举变量赋值,才不会出现类型的差异。

四:联合(共用体)

4.1联合类型的定义

联合也是一种特殊的自定义类型,这种类型定义的变量也包含一系列成员,特征是这些成员公用同一块空间

union Un
{char c;int i;
};
int main()
{union Un un;//联合变量的定义printf("%d\n", sizeof(un));//计算联合变量的大小system("pause");return 0;
}

4.2 联合的特点

联合的成员是共用同一块内存的空间,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。

union Un
{char c;int i;
};
int main()
{union Un un;printf("%d\n", &(un.i));printf("%d\n", &(un.c));system("pause");return 0;
}

4.3:联合大小的计算

  • 联合的大小至少是最大成员的大小。
  • 当最大成员大小不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍。
union Un1
{char c[5];int i;
};
union Un2
{char c[7];int i;
};
int main()
{printf("%d\n", sizeof(union Un1));//大小是8printf("%d\n", sizeof(union Un2));//大小是16system("pause");return 0;
}

我们以上对结构体,位段,联合,枚举这几种自定义类型做出了基础层面上的解读,如果各位看官有收获的话还希望给个三连支持一波,有什么意见或者建议也请评论区指出,谢谢大家!

【自定义类型】结构体,联合,枚举相关推荐

  1. 【C语言】自定义类型 结构体 枚举 联合

    结构体的基础知识 结构体是一些值的集合,这些值称为成员变量.结构体的每个成员可以是不同类型的变量. 结构体的声明 定义一个学生的结构体 struct Stu {char name[20];//名字in ...

  2. 自定义类型:结构体2.0(初阶+进阶)+位段+枚举+联合

    目录 一.结构体

  3. C语言--自定义类型——结构体

    结构体 建立结构体声明 定义结构变量 初始化结构 访问结构体成员 结构的初始化器 结构数组 指向结构的指针 用指针访问成员 向函数传递结构的信息 传递结构成员 传递结构的地址 传递结构 结构和结构指针 ...

  4. 自定义类型: 结构体,枚举,联合

       1.结构体    个人认为结构体和数组特别相似,只不过结构体和数组的区别在于结构体的成员可以是不同类型,而数组成员类型是相同的.    (1)结构体的声明 struct tag {成员列表//至 ...

  5. C语言自定义类型的介绍(结构体,枚举,联合体,位段)

    ⭐️前面的话⭐️ 大家好!在C语言中,有个叫"自定义类型"玩意,它究竟是什么呢?其实,就是字面意思,可以自己定义的类型就是自定义类型.具体说就是我们熟知的结构体,枚举,位段,联合体 ...

  6. 关于结构体,枚举,联合的一些知识

    关于结构体,枚举,联合的一些知识 首先我们来看一下什么是结构体 通俗来说,结构体便是各种类型的集合. 接下来便是各处所代表的含义了 例如,我们现在来描述一个人. 其中name,age,sex,addr ...

  7. 一文道破 结构体,枚举,联合

    自定义类型:结构体,枚举,联合 结构体 结构体类型的声明 1. 结构的基础知识 2.特殊的声明 3.结构体的自引用 4.结构体的定义和初始化 5.结构体内存对齐 6.修改默认对齐数 7.结构体传参 位 ...

  8. SV学习笔记—结构体及枚举类型及字符串

    目录 1.结构体 1.1 压缩结构体 1.2 非压缩结构体 1.3联合结构体 2.枚举类型 3.字符串 1.结构体 Verilog的最大缺陷之一是没有数据结构,SV中可以使用struct语句创建结构, ...

  9. C语言自定义变量类型——结构体详解

    文章目录 前言 为什么要学习结构体 什么是结构体 一.结构体:struct 1.结构体类型的声明 (1)结构体的基础知识 (2)结构体的声明 (3)特殊的声明 2.结构体的自引用 3.结构体变量的定义 ...

  10. 结构体,枚举类型与联合体

    目录 ​编辑 一.结构体的基本使用 1.什么是结构体 2.结构体的一般声明 3.结构体的特殊声明 4.结构体的自引用 5.结构体变量的定义和初始化 二.结构体内存对齐 1.内存对齐规则 3.为什么会存 ...

最新文章

  1. Andriod绘图的基础知识
  2. Mac 新建unix可执行文件
  3. C#进行MapX二次开发之鹰眼实现
  4. @Transactional 使用
  5. iOS 直接使用16进制颜色
  6. Coursera自动驾驶课程第13讲:Least Squares
  7. 动画原理——用户交互:移动物体
  8. 使用Redis和Apache Kafka处理时间序列数据
  9. 安卓游戏时禁止状态栏下拉,如何在Android中禁用通知栏下拉菜单?
  10. linux内存管理_架构师必读:Linux 的内存分页管理
  11. jdk1.8中文帮助文档
  12. 软件定义和硬件重构知多少(三)
  13. 实现Promise的resolve/reject/then/all/race/finally/catch方法
  14. PostgreSQL checksum与Data Corruption
  15. 中国联通沃商店校园大使招募书
  16. ASP完美优化(不断更新)
  17. 成功的路上会布满荆棘
  18. 祖传Python代码
  19. vb 彩牛概率统计for Excel
  20. 2021.10.02超详细实现过程-Linux 环境下的简易聊天室,采用CS模型,实现多客户端之间的稳定数据传输。(添加好友,删除好友、屏蔽好友、查看好友列表(针对数据库的操作))

热门文章

  1. NEUQ-acm 预备队训练Week11——复习
  2. 【NIO与Netty】网络编程:netty中粘包、半包现象展示,分析及解决
  3. undefined相关
  4. 全国省市县区乡镇街道村居委会五级行政区域数据库
  5. 程雷被机器人_太意外了:49岁知名主持人程雷,得遗传性病和女友终分手!
  6. 公众号开发——自动回复功能
  7. Robot Framework 介绍
  8. Rest Framework第二天-序列化组件(下)、解析器
  9. GDOI2007模拟-打鼹鼠
  10. 漫画女生怎么画(一步步教你画漫画)