1.数据类型分类:
![](https://i-blog.csdnimg.cn/blog_migrate/38ae67dc79674f842746b5f70f285d0d.png)
2.结构体:
①结构的基础知识:结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量
②结构的声明:
![](https://i-blog.csdnimg.cn/blog_migrate/064dccf5212334cefb8e40ccd1f4ef81.png)
举例:假如描述一个学生
情况一:声明的同时定义结构体变量
![](https://i-blog.csdnimg.cn/blog_migrate/ab77bac9c49cd9432488ac7c08c324ca.png)
情况二:声明的同时不定义结构体变量,并在之后定义
![](https://i-blog.csdnimg.cn/blog_migrate/f045d4a7adc7b9524837069e898ce8d9.png)
③特殊的声明:在声明结构的时候,可以不完全的声明(即匿名结构体类型)
![](https://i-blog.csdnimg.cn/blog_migrate/5055d2bfcdf9f858164d5afa0b25c493.png)
解释:
![](https://i-blog.csdnimg.cn/blog_migrate/de8add71b8c5516c5c4acff0959da661.png)
④问题:
![](https://i-blog.csdnimg.cn/blog_migrate/8f679103420da6753a126402e25a2feb.png)
上面的两个结构在声明的时候省略掉了结构体标签(tag)。
那么问题来了?
![](https://i-blog.csdnimg.cn/blog_migrate/a16b797f644d4c41b962f925ac876b6e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/21f39eb194fd12e662b0ef4f70a6cadd.png)
注:匿名结构体的成员如果一样,在编译器看来也是不同的类型的结构体!
⑤:为⑥的自引用理解做准备:
![](https://i-blog.csdnimg.cn/blog_migrate/0d0239f07035a3db9c1402c9cec9b532.png)
⑥结构的自引用:
错误的自引用写法:
![](https://i-blog.csdnimg.cn/blog_migrate/89a251fe91c587c3e8f9228c85188d90.png)
这种声明是错误的,因为这种声明实际上是一个无限循环,成员b是一个结构体,b的内部还会有成员是结构体,依次下去,无线循环。在分配内存的时候,由于无限嵌套,也无法确定这个结构体的长度,所以这种方式是非法的。
正确的自引用写法:
![](https://i-blog.csdnimg.cn/blog_migrate/d57e9784ca3e5900b665bf58f340a06c.png)
由于指针的长度是确定的(在32位机器上指针长度为4),所以编译器能够确定该结构体的长
⑦结构体声明时前面用typedef修饰:
![](https://i-blog.csdnimg.cn/blog_migrate/bba36b18efda16484310a74d78716035.png)
这样写代码,可行否?
![](https://i-blog.csdnimg.cn/blog_migrate/54ebe184d11347e01a7828a5186aee81.png)
这里的目的是使用typedef为结构体创建一个别名Node。但是这里是错误的,因为类型名的作用域是从语句的结尾开始,而在结构体内部是不能使用的,因为还没定义。
![](https://i-blog.csdnimg.cn/blog_migrate/05dff3d1fad7d75e1d98e5baa7387fa5.png)
⑧typedef修饰结构体时出现指针:
![](https://i-blog.csdnimg.cn/blog_migrate/d8421cb339753236be0ec28547e3a332.png)
⑨结构体变量的定义和初始化
情况1:只声明不定义
![](https://i-blog.csdnimg.cn/blog_migrate/9419d1bd244bc52819e03ee0ba7b1943.png)
情况2:声明后定义
![](https://i-blog.csdnimg.cn/blog_migrate/4911485a64421343ef78c9a6017361c8.png)
情况3:既定义又初始化
![](https://i-blog.csdnimg.cn/blog_migrate/a062c381d9a9bcde147d9957af14bc62.png)
情况4:结构体的嵌套
![](https://i-blog.csdnimg.cn/blog_migrate/eed7f199a7224492405361b4139aebdf.png)
⑩结构体内存对齐--计算结构体的大小
![](https://i-blog.csdnimg.cn/blog_migrate/f7308e71a31c0db11bafa5fd5f490f22.png)
内存对齐规则应用:
![](https://i-blog.csdnimg.cn/blog_migrate/dd8ba3b84c988a266081e21b814cbb0c.png)
输出结果:8
我们来分析一下为什么结果是 8??
![](https://i-blog.csdnimg.cn/blog_migrate/2a10ea7df278014daeaf2a04333b837e.png)
我们来看一下内存的分布图:
![](https://i-blog.csdnimg.cn/blog_migrate/7be9df21cb6e4bf13c7aa34b517f282e.png)
那我们再来看一下这个输出结果是多少呢?
![](https://i-blog.csdnimg.cn/blog_migrate/d326b89dfa42e006b7a850c502862724.png)
输出结果:12
我们来分析一下为什么结果是 12??
![](https://i-blog.csdnimg.cn/blog_migrate/8c4b2cf1fc633da5929a602f423eeae7.png)
我们来看一下内存的分布图:
![](https://i-blog.csdnimg.cn/blog_migrate/4c1bf821627a30d8276c2713300df270.png)
结构体也是可以嵌套使用的,那如果嵌套的话,大小是怎么判断的呢?
![](https://i-blog.csdnimg.cn/blog_migrate/f6044c25cd363be327a2c09312282d39.png)
输出结果:32
我们来分析一下为什么结果是 12??
![](https://i-blog.csdnimg.cn/blog_migrate/5ded249c3860b20bc9bfc78faa9c7e10.png)
我们来看一下内存的分布图:(图有点问题,s3对齐处应该是8)
![](https://i-blog.csdnimg.cn/blog_migrate/724ad8f2a8dda01fac5a7e5150315f08.png)
为什么存在内存对齐呢?
![](https://i-blog.csdnimg.cn/blog_migrate/180c8b5626ca6a05dd47a3e61e17f27a.png)
解释访问:
![](https://i-blog.csdnimg.cn/blog_migrate/e4bc021ce33ae50b85660dac981e60bd.png)
⑪结构体属性的偏移量计算——关于offsetof函数的详解
offsetof是一个宏,作用是计算结构体成员相对于起始位置的偏移量,使用时引用头文件stddef.h
案例一:
![](https://i-blog.csdnimg.cn/blog_migrate/aae940facfd56c92ce140ee6e65fd602.png)
输出结果:
![](https://i-blog.csdnimg.cn/blog_migrate/12fe7461dfa3d3bd1e0c00f5be6ca646.png)
案例二:
![](https://i-blog.csdnimg.cn/blog_migrate/4ea2ee821b0b50b5176094d10f7a5404.png)
输出结果:
![](https://i-blog.csdnimg.cn/blog_migrate/fb5ccbd061a48cbba421ce9b16472e47.png)
⑫那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:
让占用空间小的成员尽量集中在一起
案例:
![](https://i-blog.csdnimg.cn/blog_migrate/8787c1ad64277d27dc6b218cfdf105e9.png)
S1和S2类型的成员一模一样,但是S1和S2所占空间的大小有了一些区别
⑬.修改默认对齐数
之前我们见过了 #pragma 这个预处理指令,这里我们再次使用,可以改变我们的默认对齐数
![](https://i-blog.csdnimg.cn/blog_migrate/cc014b47385c0d27ecc915be17522c57.png)
注:①#pragma pack(1)等价于内存不对齐
②#pragma pack(n) n可以取(1, 2, 4, 8, 16)
⑭结构体传参
![](https://i-blog.csdnimg.cn/blog_migrate/7a5f08ed281e64456e8af6aee4c70510.png)
上面的 print1 和 print2 函数哪个好些?
答案是:首选print2函数
原因:
函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。 如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的 下降
结论:
结构体传参的时候,要传结构体的地址。
3.位段:
①什么是位段:
C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为位段。利用位段能够用较少的位数存储数据。
②位段的声明和结构是类似的,但有两处不同
![](https://i-blog.csdnimg.cn/blog_migrate/cd5be00ed90a1638533508599eba7520.png)
案例:
![](https://i-blog.csdnimg.cn/blog_migrate/0ea2f830ab39a36ce06e457882fe87fb.png)
输出结果:
![](https://i-blog.csdnimg.cn/blog_migrate/6243c4ae19ebc7085a6da75ab4434514.png)
解释:
![](https://i-blog.csdnimg.cn/blog_migrate/1616e08aa07bf500ee68a3c3787577c9.png)
③位段的内存分配:
![](https://i-blog.csdnimg.cn/blog_migrate/5429a307526d75c73252a8aa92ba2514.png)
![](https://i-blog.csdnimg.cn/blog_migrate/49122fbdfad2c6bb9bed499f1db17979.png)
案例:
![](https://i-blog.csdnimg.cn/blog_migrate/e7374f1518045260b2efb81c3ae99764.png)
解释:
![](https://i-blog.csdnimg.cn/blog_migrate/bccd41802a1beefb59780b5157cfc30d.png)
④位段的跨平台问题
![](https://i-blog.csdnimg.cn/blog_migrate/8fa850068c5c1773e3cabe924be0c1eb.png)
![](https://i-blog.csdnimg.cn/blog_migrate/b443b1c3652624fb8c5982d9748b3194.png)
⑤位段的存储和大小端的区别
![](https://i-blog.csdnimg.cn/blog_migrate/085dc3f582438020d4afe3a52b804f8b.png)
⑥位段的应用
![](https://i-blog.csdnimg.cn/blog_migrate/0257df14eeeed364f76a3bcf41237b53.png)
⑦位段占的二进制位数不能超过该基本类型所能表示的最大位数,即位段不能跨字节存储,比如在char是占1个字节,那么最多只能是8位
案例:
![](https://i-blog.csdnimg.cn/blog_migrate/a51e2c071f7e58be5c05ac4cb9188c82.png)
⑧在位段中不给其起名字,无法访问该位段,但是计算大小是仍占空间
案例:
![](https://i-blog.csdnimg.cn/blog_migrate/ea6d9d9cbb602798afe50047f636d896.png)
输出结果:
![](https://i-blog.csdnimg.cn/blog_migrate/0972caa19f44f467c2f1b8b30983021b.png)
⑨不可对位段进行取地址操作
案例:
![](https://i-blog.csdnimg.cn/blog_migrate/c1dfbcd69f535926b479e02868c4e471.png)
输出结果:
![](https://i-blog.csdnimg.cn/blog_migrate/15f5ec95d9cefd0f187497003ffe8985.png)
⑩如果位段中存储的数据大于位段自身大小就会发生截断问题(在vs2013中测试的结果),在输出时会将截断之后的最高位当作符号位
案例:
![](https://i-blog.csdnimg.cn/blog_migrate/1b1d27b7aba37fe370ed7775ea23a4ed.png)
输出结果:
![](https://i-blog.csdnimg.cn/blog_migrate/961000935a5d725c03839b14b544b3f4.png)
解释:
![](https://i-blog.csdnimg.cn/blog_migrate/d970ef21ad1a7648a440cdcfa047bf43.png)
如果将位段的类型换为无符号的:
![](https://i-blog.csdnimg.cn/blog_migrate/a79864a5608af5a3496a22c9eb3936dd.png)
输出结果:
![](https://i-blog.csdnimg.cn/blog_migrate/fdea9b9821cb05e794ee1a7b0159bcdc.png)
我们接下来再用u%输出试一下:
![](https://i-blog.csdnimg.cn/blog_migrate/150c4dc844ece3f9dd6a83c65d1f1751.png)
输出结果:
![](https://i-blog.csdnimg.cn/blog_migrate/5b1cdcc414148de1a1b11f9b8d79e0ff.png)
4.枚举:
![](https://i-blog.csdnimg.cn/blog_migrate/ec6373a34b95f238795e1cfb043d2fb7.png)
①定义格式为:
![](https://i-blog.csdnimg.cn/blog_migrate/fffbe5b044d1e65e4d7db7444f8038d9.png)
案例Ⅰ:
![](https://i-blog.csdnimg.cn/blog_migrate/ebe5be43bc62c483133d832047966131.png)
案例Ⅱ:
![](https://i-blog.csdnimg.cn/blog_migrate/dc542261e103ec7f12cef05bc89ab214.png)
案例Ⅲ:
![](https://i-blog.csdnimg.cn/blog_migrate/0a97560d4b595d6f81261c3b0e6e1bd8.png)
②以上定义的 enum Day , enum Sex , enum Color 都是枚举类型
{}中的内容是枚举类型的可能取值,也叫 枚举常量 。 这些可能取值都是有值的,默认从0开始,一次递增1,当然在定义的时候也可以赋初值
案例:
![](https://i-blog.csdnimg.cn/blog_migrate/1b5d35478fae371f538c06449aeb3bcb.png)
输出结果:
![](https://i-blog.csdnimg.cn/blog_migrate/084fd477d19d91ec1f0dccdf5de621bc.png)
案例:
![](https://i-blog.csdnimg.cn/blog_migrate/b03e7920da47f046d30c91a0f7d4f8ae.png)
输出结果:
![](https://i-blog.csdnimg.cn/blog_migrate/90148f7b07bd11a813e3a3106876fd0b.png)
③枚举的使用:(没搞懂,要问一下老师)
![](https://i-blog.csdnimg.cn/blog_migrate/5dd97e009c1458aaf9ba16f7d7e40bdd.png)
5.枚举的优点
![](https://i-blog.csdnimg.cn/blog_migrate/fc1669c93509fa5d038fa2a85864bef7.png)
①为什么使用枚举:
![](https://i-blog.csdnimg.cn/blog_migrate/ac0dfe1ee4cdeaebccf79906101d13f7.png)
②便于调试
![](https://i-blog.csdnimg.cn/blog_migrate/5d5aa6f0d93c1576eaceb88013f040e8.png)
6.
结构体的关键字:struct
枚举的关键字:enum
联合体(共用体)的关键字:union
7.联合体:
①联合体的定义:联合体是一种特殊的自定义类型,联合体中的成员可以共用一部分内存,所以也叫做共用体
②联合体的声明和定义如下:
![](https://i-blog.csdnimg.cn/blog_migrate/338439a909d9a6b964779ccc00016276.png)
③内存占用情况:
![](https://i-blog.csdnimg.cn/blog_migrate/7ee2808c1ba5b82cf051db747b4bac05.png)
④联合体的特点:
![](https://i-blog.csdnimg.cn/blog_migrate/b3630559510b9bfa31cb1d9f2a59d3ef.png)
⑤证明:
![](https://i-blog.csdnimg.cn/blog_migrate/5fb9f7146aea31ed4409ca63ec52a16a.png)
输出结果:
![](https://i-blog.csdnimg.cn/blog_migrate/d7cfef719972de41bbd323661f71b2d7.png)
⑥联合体大小的计算:
![](https://i-blog.csdnimg.cn/blog_migrate/201b8d692fc2060f88185dff89bd3abc.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ef565c4a544b5e69d2b7986dd926ec50.png)
![](https://i-blog.csdnimg.cn/blog_migrate/a7f89f4e6af3e6aba77c82c0a8d45175.png)
⑦判断联合体的大小端
以前判断的方法:
![](https://i-blog.csdnimg.cn/blog_migrate/cd92e5c3e97d54f4a323c2504cb08960.png)
输出结果:
![](https://i-blog.csdnimg.cn/blog_migrate/1af91eeee42cf6c986f62b4376e1d33d.png)
联合体方法:
![](https://i-blog.csdnimg.cn/blog_migrate/c16c066238560bb7b894f22a0b543247.png)
输出结果:
![](https://i-blog.csdnimg.cn/blog_migrate/5c5f6fc739661542f7a37fc1b637e2ab.png)
解释:
![](https://i-blog.csdnimg.cn/blog_migrate/1c7c12a2115cdb5ff2fc5ff56c8630d3.png)