你们的每个赞都能让我开心好几天✿✿ヽ(°▽°)ノ✿
构造型数据类型与应用
前言
很多文章老是只讲概念不讲例题,或者说就讲一两道,初学者不容易总结规律,我总结了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 %d\n",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("%d\n",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
2.结构体与共用体的字节对齐
字节对齐原则:字节为数据基本类型的字节的整数倍,比如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:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
struct ks
{
int a:4;
int b:3;
};
int main()
{
struct ks e;
e.b=3;
e.b++;
printf("%d %d",sizeof(e),e.b);
}
- 虽然a只取4位二进制数,b只取3位二进制数,看起来连1个字节也不到(注:1个字节可以存8位二进制数),但是由于int占4个字节,位段只是将好几个int型变量压缩至一个int存储单元,存不满就存到一个int里,存不下就存至下一个int存储单元。故一共还是占4个字节。
- 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("%x\n",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:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
int main()
{
int a,b;
a=520;
a=a>>8;
b=~(~0<<4);
printf("%d",a&b);
}
- 熟练掌握位运算的规则和二进制与十进制的转换即可。
答案:2
总结
实际上,以上的内容只是在考试中有用,实际中,可能不会有太大的用处,不过多懂一些总是好的
最后,如果这篇文章对你有帮助,就点个大拇指吧!
你们的每个赞都能让我开心好几天✿✿ヽ(°▽°)ノ✿