你们的每个赞都能让我开心好几天✿✿ヽ(°▽°)ノ✿
构造型数据类型与应用文章目录 构造型数据类型与应用前言一、学习内容:二、正文部分(以下问题皆为输出的结果)1.结构体与共用体的内容存储方式及其区别2.结构体与共用体的字节对齐3.结构体与位段的结合例题4.位运算 总结
前言
很多文章老是只讲概念不讲例题,或者说就讲一两道,初学者不容易总结规律,我总结了10道经典例题,如果同学们能明白例题,那么相关概念再看就会清楚许多了。这10道例题都是我在老师的云班课、实验报告册上精心总结改编而来的,题目质量很不错,图文结合
提示:最重要的是画图理解字节!
一、学习内容: 结构体与共用体的内容存储方式及其区别结构体与共用体的字节对齐结构体与位段的结合例题位运算 二、正文部分(以下问题皆为输出的结果) 1.结构体与共用体的内容存储方式及其区别(1)例1:共用体的数据的具体存储方式(联系二进制)
#include<stdio.h>#include<stdlib.h>#include<string.h>#include<stdio.h>union unit{int a;char c[3];}; int main(){union unit T;T.a=1;printf("%d %dn",T.c[0],T.c[1]);}共同体与结构体最大的不同是,共用体是将逻辑相斥的数据类型组合在一起,这些不同类型的成员在内存中所占用的起始单元是相同的,因此,用共用体会省空间,但是也会涉及到一个字节覆盖问题。
首先,我建议画字节的时候从右往左画,这是因为咱们写二进制数的时候,二进制的最低位在最右边。
其次,根据共用体中定义数据类型的顺序依次从上往下画。
然后我们来分析一下这个具体过程。
T.a=1时会让int a这4个字节被赋值为0000…(一串0)01,这是1的二进制数,于此同时,char c[3]这三个字节会被覆盖,这是因为它们起始内存单元是相同的,会共同占据一定的空间。注意:我分开int a 与char c[3]画图是为了方便观察分析,实际上,它们是重叠在一起的,共同使用前三个字节。因此,char c[3]这三个字节会被覆盖,其字节同样被赋值为0000…(一串0)01。
最后,c[0]处的二进制为00000001,c[1]为00000000.(从右往左的序号是0,1,2),故输出 1 与 0.
答案:1 0
例2:共用体的数据的具体存储方式(联系二进制)
#include<stdio.h>#include<stdlib.h>#include<string.h>union example{char nA;int nB;}e;int main(){e.nA='a';printf("%dn",e.nB);e.nB=128;e.nB=e.nA+e.nB;printf("%d",e.nB);} :这道题略有些绕,而且容易出错,待我慢慢分析。
大家可以一起试着画图
第一次赋值e.nA=‘a’;实际上赋值的ASCII码为97,其二进制为01100001,由于共用体共用一段内存,故nB的二进制也被赋值为了01100001(注:左边三个字节没有被覆盖到的,二进制默认为0)。直接输出e.nB,那么也会是97.
第二次赋值e.nB=128,其二进制为10000000,在int类型中貌似没什么特殊的,但是,在只有1个字节的char中,它只能存8位二进制数,取值范围是-128~127,并且首位二进制数为符号位,1为负,0为正。127的二进制数为01111111,加1之后是10000000,即-128而非128!所以,将e.nA的实际数值是-128
最后,e.nA加e.nB即是-128+128,即为0。输出0
- 答案: 97 0
(2)例3:共用体与结构体的共同作用:
#include<stdio.h>#include<stdlib.h>#include<string.h>struct ex{union un{int a;int b;}in;int x;}e;int main(){e.x=10; e.in.a=3;e.x=e.in.a+e.in.b;printf("%d %d",e.x,e.in.a);}区别:结构体成员的存储空间是连续的,共用体成员的存储空间是共用的
故画图如下:
第一次赋值e.x=10;不影响共用体。
第二次赋值e.in.a=3;会影响共用体,但是不影响结构体。覆盖e.in.b为3.
第三次赋值直接数值相加即可,e.x=3+3=6。
- 答案:6 3
字节对齐原则:字节为数据基本类型的字节的整数倍,比如int,double,float,long int,char,依据这个补字节*
(1)例4:
#include<stdio.h>#include<stdlib.h>#include<string.h>#include<stdio.h>typedef union{double i;int k[5];char c;}DATE;DATE max;struct date{int cat;DATE cow;double dog;}too;int main(){printf("%d",sizeof(struct date)+sizeof(max));} 在共用体DATE max中:double i占据8个字节,int k[5]占据4*5=20个字节,char c占据1个字节。按共用体的共占据内存来说,应该占据数据成员最大字节数即20个字节,但是实际上,由于字节对齐原则,会将字节补至数据基本类型的字节的整数倍,即这个总的字节既要是double的整数倍,又要是char的整数倍,(注:数组不是基本数据类型,是叠加起来的数据类型了),故:应该字节补齐至24个字节在结构体struct date中:int cat占据4个字节,DATE cow占据24个字节,double dog占据8个字节,按结构体的数据成员连续占据内存规则来说,应该是将各个字节数相加,即4+24+8=36个字节。但是实际上,由于字节对齐原则,要补至double与int类型的整数倍,故补齐至40个字节最后24+40即可答案:64
例5:将上述代码的char c改为int c,你是否会做呢?
#include<stdio.h>#include<stdlib.h>#include<string.h>#include<stdio.h>typedef union{double i;int k[5];int c;}DATE;DATE max;struct date{int cat;DATE cow;double dog;}too;int main(){printf("%d",sizeof(struct date)+sizeof(max));}思考过程留给大家
- 答案:64
3.结构体与位段的结合例题鉴于大家对位段可能不太熟悉,先复习一遍
①位段只能用于unsigned或int类型
②“:”冒号之后的数字必须是1~8范围内的数
③位段成员不能是数组
④unsigned:0有特殊含义,使紧随其后的下一个位段成员存储在新起的一个字节里,无论上一个字节多少位
⑤一个位段成员必须存储在一个字节单元内,不能跨单元
⑥可以为位段类型定义无名成员
(1)例6:
e.b的分析如图
答案:4 -4
(2)例7:
#include<stdio.h>struct bitdata{unsigned a:2;unsigned b:3;unsigned:0;unsigned c:3; };int main(){printf("%d",sizeof(bitdata));}由于unsigned : 0,故存完a,b后舍弃剩余的unsigned空间,存到下一个unsigned中,故输出8
答案:8
(3)例8:
#include<stdio.h>struct bitdata{unsigned a:2;unsigned b:3;unsigned:24;unsigned c:3; };int main(){printf("%d",sizeof(bitdata));} 首先,位段允许无名成员。其次,2+3+24+3=32。刚好存满unsigned的4个字节,故输出4答案:4
(4)例9:
#include<stdio.h>struct bitdata{unsigned x:2;unsigned y:3; }a;int main(){a.x=11;a.y=15;printf("%xn",a.x+a.y);} 注意,unsigned是无符号的,因此二进制的最高位不是符号位。根据上图,a.x的周期为4,a.y的周期为8,故a.x=11等价于十进制中的3,a.y=15等价于十进制中的7,故加起来为10最后注意输出%x是十六进制的意思,10的十六进制为a。故输出a- 答案:a 4.位运算
先复习一下位运算:
位运算:对象是二进制
①按位与&
②按位或|
③按位异或^
④按位取反~
⑤按位左移<<:低位补0高位舍弃
⑥按位右移>>:正数:低位舍弃高位补0。负数:有的编译系统会补0(逻辑右移),有的编译系统补1(算数右移),VC++与GCC(我用的)均采用算数右移,比如-2>>2的值为1
(1)例10:
答案:2
总结实际上,以上的内容只是在考试中有用,实际中,可能不会有太大的用处,不过多懂一些总是好的
最后,如果这篇文章对你有帮助,就点个大拇指吧!
你们的每个赞都能让我开心好几天✿✿ヽ(°▽°)ノ✿