专栏:C语言
个人主页:HaiFan.
专栏简介:本专栏主要更新一些C语言的基础知识,也会实现一些小游戏和通讯录,学时管理系统之类的,有兴趣的朋友可以关注一下。

结构体

  • 前言
  • 一、结构体
    • 1.结构体类型的声明
    • 2.结构体的自引用
    • 3.结构体变量的定义和初始化
    • 4.结构体内存对齐
      • 3.1修改默认对齐数
    • 5.结构体传参
  • 二、位段
    • 1.什么是位段
    • 2.位段的内存分配
    • 3.位段的跨平台问题
  • 三、枚举
    • 1.枚举类型的定义
    • 2.枚举的优点
  • 四、联合(共用体)
    • 1.联合类型的定义
    • 2.联合的特点
    • 3.联合体大小的计算

前言


一、结构体

1.结构体类型的声明

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

struct tag//结构体的名字
{member-list;//这是结构体的成员
}variable-list;//这就是结构体的声明

如描述运动和人数

struct sport
{int run;//跑步int soccer;//足球int badminton;//羽毛球char name[100];//姓名
};//分号不能丢

这里的run,soccer,badminton,name,都是成员变量

结构体中,有一个特殊的声明,就是在声明结构的时候,可以不完全声明。
如:

//匿名结构体类型
struct
{int a;char b;float c;
}x;

上面的结构体在声明的时候,省略了结构体的标签(tag),这种结构体被称为匿名结构体。

2.结构体的自引用

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

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

这样做,可行吗?

很显然不可行。那么正确的用法是是什么呢?

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

在自引用中,若使用结构本身的成员,每次在调用结构体的时候,会发生嵌套,所以应该使用指针类型。指针就是地址,不会发生嵌套。


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

这两个代码,哪一个是合法的呢?答案是第二个,因为第一个结构体成员是Node* next;在调用该next成员的时候,typedef还没有起到作用。

3.结构体变量的定义和初始化

有了结构体的成员,该如何定义变量?其实很简单。

struct Point
{int x;
int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2

这是定义结构体的两种方式。


结构体的初始化

struct Stu //类型声明
{char name[15];//名字int age; //年龄
};
struct Stu s = {"zhangsan", 20};//初始化

这就是结构体的初始化。

struct Stu s;
scanf("%s %d",s.name, &s.age);

也可以通过自己输入,进行初始化。

4.结构体内存对齐

前面已经介绍了结构体的基本使用,那么如何计算结构体的大小呢?我们知道int4字节,char1字节,double8字节

struct Stu
{char name[10];int age; double len;float w;long a;long long c;
};

我们知道不同的数据类型占多少字节,那么这个结构体的大小是10+4+8+4+4+8=38吗?

很奇怪,结果并不是所有成员所占的字节数相加。下面为大家介绍:结构体的内存对齐,这是一个特别热门的考点。

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

  1. 第一个成员在与结构体变量偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的值为8
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
struct S1
{char c1;int i;char c2;
};
printf("%d\n", sizeof(struct S1));

char占1个字节,int占4个字节,根据规则1,要先从偏移量为0的地址处开始。再根据规则2,找到int的倍数的位置开始对齐,然后在找最后一个c2的位置,除了0,其他数都是1的倍数,所以c2对齐到8的位置即可,现在结构体的大小为9,再根据规则3,结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍,所以要继续扩大,直到找到所有成员中最大对齐数的整数倍为止,也就是12.

struct S2
{double d;char c;int i;
};
printf("%d\n", sizeof(struct S2));

这个结构体的大小是多少?
首先,double占8个字节,从0的位置开始对齐,后面的char占1个字节,对齐到double后面的位置即可,然后就是int占4个字节,先找到4的倍数的位置,开始对齐,然后看此时的大小是不是结构体成员中对齐数最大的数的倍数,如果是,则此时的大小就是结构体的大小,若不是,则继续对齐,直到找到结构体成员中对齐数最大的数的倍数为止。

此时算出来的大小为16,16是double 8的倍数,所以16就是该结构体的大小。

struct S2
{double d;char c;int i;
};
printf("%d\n", sizeof(struct S2));//16struct S3
{char c1;struct S2 s2;double d;
};
printf("%d\n", sizeof(struct S3));

那么嵌套的结构体的大小是多少呢?
还是老规矩,先把c1对齐到0的位置处,然后根据规则4,先找到s2结构体里的最大对齐数,在找到该数的倍数处,开始对齐,注:这里就不是在对齐8个,而是对齐s2结构体的大小16个,之后在对齐最后一个double d。


此时结构体的大小是31,但是并不满足规则3,继续对齐,最后结构体的大小就是32.


为什么存在内存对齐?

大部分的参考资料都是如是说的:
1.平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特 定类型的数据,否则抛出硬件异常。
2.性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。性能原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访 问。

总体来说:
结构体的内存对齐是拿空间来换取时间的做法。

那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:
让占用空间小的成员尽量集中在一起。

3.1修改默认对齐数

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

#include <stdio.h>
#pragma pack(8)//设置默认对齐数为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));printf("%d\n", sizeof(struct S2));
}

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

5.结构体传参

直接看代码:

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); //传地址return 0;
}

上面的print1和print2哪一个更好?
答案是:print2.

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

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

二、位段

1.什么是位段

位段的声明和结构是类似的,但有两个不同。
1.位段的成员必须是int,unsigned in,或者signed int。
2.位段成员名后边跟一个冒号和一个数字。

struct stu
{int _people : 2;int _run : 5;int _go : 10;
};

stu就是一个位段类型。
那么位段stu的大小是多少呢?
printf("%d", sizeof(struct stu));

答案是4,为什么会是4呢?
int占4字节,32个bit位,而位段里面的2,5,10,表示的是占多少bit位,成员一占2个bit位,成员而占5个bit位,成员3占10个bit位,总共占17个bit位,没有超过int的大小,所以是四个字节。

struct stu
{int _people : 2;int _run : 5;int _go : 10;int _a : 30;
};

那么这个printf("%d", sizeof(struct stu));是多少呢?

按照前面的分析方法,前三个成员总共占17个bit位,第四个成员占30个bit位,一个int是不够存放的,所以要在开辟一个空间,存放第四个成员,所以得到的结果是8.

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;

那么这些数值是如何在位段中进行内存分配的呢?
一共是8个字节。

先把10的补码写出来,00001010,因为a只要3个bit位,所以就发生了神奇的现象——截断,所谓截断,就是只把前三个bit位拿出,后面的全部不要。就得到了

在把12的补码写出来,00001100,在发生截断,只要前4个bit位,

在把3的补码写出,00000011,截断,只要前5个bit位,因为一个char已经无法满足继续存c截断之后的补码了,所以要开辟一个新空间来存放c。

同样,再按照相同的规则存放d,即可。int类型也是这样。

3.位段的跨平台问题

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

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

三、枚举

什么是枚举,枚举就是遍历 ,比如一到十 一 一 列举出来。
只要是可以 一 一 列举出来的,都可以使用枚举。

1.枚举类型的定义

enum Sex
{man,woman
};

这就是枚举类型,{}中的内容是枚举的可能取值,也叫做枚举常量。

注:用逗号隔开,最后一个枚举常量后面没有符号

当然,这些取值都是有值的。

默认从0开始,每次都会递增1,当然在定义的时候也可以赋值。

enum Sex
{man = 1,woman = 4
};

2.枚举的优点

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

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

后面我会用枚举和动态内存来优化通讯录。

四、联合(共用体)

1.联合类型的定义

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

union u
{int a;char b;
};int main()
{union u de;printf("%d", sizeof(de));return 0;
}


由此可见,a和b共用同一块空间。空间是数据类型最大的那一个。

2.联合的特点

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

3.联合体大小的计算

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

最大成员的大小是c,占5个字节,这里面的最大对齐数是4字节,因为5不是4的倍数,所以要进行对齐,直到找到最大对齐数的整数倍的位置即可,也就是8.

C语言——自定义类型详解(结构体,联合体,枚举,位段)相关推荐

  1. C语言自定义类型详解

    C语言自定义类型详解 一.结构体 1.结构的声明 2.特殊的声明 3.结构的自引用 4.结构体变量的定义和初始化 5.结构体内存对齐 6.修改默认对齐数 7.结构体传参 二.位段 1.什么是位段 2. ...

  2. C语言自定义类型详解!

    目录 内容概述 结构体类型 结构类型的声明 结构体的特殊声明 结构体的自引用 结构体变量的定义和初始化 结构体内存对齐 结构体传参 位段 什么是位段? 位段的内存分配 位段跨平台问题 枚举类型 枚举类 ...

  3. C语言struct关键字详解—结构体

    struct 是个神奇的关键字,它将一些相关联的数据打包成一个整体,方便使用. 在网络协议.通信控制.嵌入式系统.驱动开发等地方,我们经常要传送的不是简单的字节流(char 型数组),而是多种数据组合 ...

  4. 自定义类型详解:结构体(内存对齐、位段) + 枚举 + 联合

    目录 一.结构体 1.特殊的声明 2.结构体自引用 3.结构体变量的定义和初始化 4.打印结构体 二.==结构体内存对齐== 1.内存对齐 结构体嵌套如何求 为什么存在内存对齐? 2.修改默认对齐数 ...

  5. 黑马程序员C语言基础(第八天)复合类型(自定义类型)(结构体)、共用体(联合体)、枚举enum、 typedef

    黑马程序员C语言基础(第一天) 黑马程序员C语言基础(第二天) 黑马程序员C语言基础(第三天) 黑马程序员C语言基础(第四天)数据类型 黑马程序员C语言基础(第五天)运算符与表达式.程序流程结构.数组 ...

  6. 你是真的“C”——详解结构体知识点

    你是真的"C"--详解结构体知识点

  7. C语言指针结构体详解,结构体指针,C语言结构体指针详解

    结构体指针,可细分为指向结构体变量的指针和指向结构体数组的指针. 指向结构体变量的指针 前面我们通过"结构体变量名.成员名"的方式引用结构体变量中的成员,除了这种方法之外还可以使用 ...

  8. c语言结构体指针详解,结构体指针,C语言结构体指针详解

    结构体指针,可细分为指向结构体变量的指针和指向结构体数组的指针. 指向结构体变量的指针 前面我们通过"结构体变量名.成员名"的方式引用结构体变量中的成员,除了这种方法之外还可以使用 ...

  9. 详解结构体、类等内存字节对齐

    先说个题外话:早些年我学C程序设计时,写过一段解释硬盘MBR分区表的代码,对着磁盘编辑器怎么看,怎么对,可一执行,结果就错了.当时调试也不太会,又根本没听过结构体对齐这一说,所以,问题解决不了,好几天 ...

最新文章

  1. 论外部调用代理应该属于那一层
  2. linux开机启动项6个级别_linux开机启动设置的几种方法
  3. WIN7 64位系统搭建WINCE6.0系统遇到的问题
  4. 太原理工软件学院c语言2020,太原理工软件工程C语言实验报告 数组.doc
  5. J-LINK序列号修改
  6. GitStats:Git开发历史统计工具 - liyropt - 博客园
  7. 三维重建12:室内三维物体的位姿识别论文列表
  8. linux datetime,Python datetime模块示例详解
  9. oracle窗帘位图索引,Greenplum数据库设计开发规范参考.docx
  10. 数据结构时间复杂度T(n)=O(f(n))的含义
  11. 分时问候并显示不用图片案例
  12. OpenCV-Python实战(18)——深度学习简介与入门示例
  13. sap 双计量单位_SAP双计量单位配置指南CUNI.doc
  14. c 语言病毒源码大全,易语言病毒源码大全
  15. 机器学习sklearn----支持向量机SVC模型评估指标
  16. 牛腩新闻发布--本地超链接打不开
  17. 现在可用:Yggdrasil图标包
  18. Excel的查找和引用函数:VLOOKUP、OFFSET、MATCH、INDEX、INDIRECT
  19. 2015——那年、匆匆
  20. Kaggle ICML2013 fer2013人脸表情识别/面部表情识别:训练、调优、调试与踩坑

热门文章

  1. 人机交互:虚拟翻书与空中翻书的种类与技术原理及案例展示
  2. 全面讲解分布式数据库架构设计特点
  3. 2022-07-06 使用tpch大数据量压测mysql
  4. 怎么将PDF转成Word
  5. XAMPP Apache关于https配置问题
  6. java基于SpringBoot+Vue的大学生体质健康测试管理系统 element
  7. 程序员通过国企面试,HR说保底17薪但不写合同,靠不靠谱?
  8. 美通企业日报 | 京沪深中高端人才月薪超2万;天津首家戴森官方体验店开幕
  9. https与http的区别以及https加密原理
  10. JS是如何实现多线程的