摘要:首先感谢三位博主,并做出总结. 首先了解一下struct的储存结构: 一、结构体的访问1.结构体成员的的直接访问,如下结构体:struct A{ int a; long *b; char c[20]; }; struct A com; 结构体成员通过操作符"."访问,表达式com.a的结果是个数组名,可以把它使用在任何可以使用数组名的地方,com.a[4],将选择一个数组元素。 2、结构体成员的间接访问 struct A *p; 可以使用(*p).a访问结构体成员,但这种形式有点不简洁所以使用操作符"->"来访问结构体成员,箭头操作符对左操作数执行间接访问来获取指针所指向的结构,然后根据右操作数来访问一个成员,p->a。 二、结构体的自引用struct B{ int a; struct B b; int c; }; 这种引用是不合法的,因为b是一个完整的结构,第二个成员又是另一个完整的结构,还包括她自己的成员,这样会循环下去无法及所结构体的大小。 struct B{ int a; struct B *b; int c; }; 这种声明是合法的,b现在是一个指针它所占的字节数是已知的,可以计算出结构体的大小,这种自引用是合法的。 三、结构体、指针和成员typedef struct{ int a; short b[2]; }Ex1; typedef struct{ int a; char b[3]; Ex1 c; struct Ex1 d; }Ex2; Ex2 x={1,"My",{2,{3,4}},0}; Ex2 *p=&x; 如下图来表示此结构: 创建一个指向整型的指针:int *p1;若要使它指向整型成员a,应使用&取得一个指向p->a的指针:p1=&p->a. 访问嵌套的结构体:p->c.a即为访问c结构体中的整形a。 访问指针成员:定义另一结构:Ex y; x.d=&y;则Ex->d->c.b[1]=4;则表示如下图空间: 四、结构体的内存对齐对齐规则
每个特定平台上的编译器都有自己的默认“对齐系数”,也可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。
规则:
1、数据成员对齐规则:struct数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。(即第一个数据成员以后的成员的偏移地址为#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个的整数倍)
2、struct的整体对齐规则:在数据成员完成各自对齐之后,结构体本身也要进行对齐,对齐将按照#pragma pack指定的数值和struct最大数据成员长度中,比较小的那个进行。
3、当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。
当结构体里面包含另一结构体时,直接将该结构体中的内容代换进去,计算其总的存储空间即可。
例:(在此平台上int占4个字节) 虽然A和A1所包含的成员相同,但A占了12个字节,A1占8个字节,所以在声明中对结构体列表的排列,因该让边界要求严格的成员首先出现(数据成员自生长度大的先出现) 以上转载自:https://www.cnblogs.com/Blog-day/p/MY_Blog_Days-3.html 1.联合体union的基本特性——和struct的同与不同 union,中文名“联合体、共用体”,在某种程度上类似结构体struct的一种数据结构,共用体(union)和结构体(struct)同样可以包含很多种数据类型和变量。 不过区别也挺明显: 结构体(struct)中所有变量是“共存”的——优点是“有容乃大”,全面;缺点是struct内存空间的分配是粗放的,不管用不用,全分配。 而联合体(union)中是各变量是“互斥”的——缺点就是不够“包容”;但优点是内存使用更为精细灵活,也节省了内存空间。
2.双刃剑——多种访问内存途径共存 一个例子了然:
所以说,管union的叫共用体还真是贴切——完全就是共用一个内存首地址,并且各种变量名都可以同时使用,操作也是共同生效。如此多的access内存手段,确实好用,不过这些“手段”之间却没法互相屏蔽——就好像数组+下标和指针+偏移一样。 上例中我改了v.i的值,结果v.l也能读取,那么也许我还以为v.l是我想要的值呢,因为上边提到了union的内存首地址肯定是相同的,那么还有一种情况和上边类似: 一个int数组变量a,一个long int(32位机中,long int占4字节,与int相同)变量b,我即使没给int变量b赋值,因为数据类型相同,我使用int变量b也完全会拿出int数组a中的a[0]来,一些时候一不小心用上,还以为用的就是变量b呢~ 这种逻辑上的错误是很难找出来的(只有当数据类型相去甚远的时候稍好,出个乱码什么的很容易发现错误)。
PS:感谢热心网友的提醒“在union定义结束时加分号”,其实是可以不加的,因为他不在主函数内,不是执行的语句,如果是主函数内声明的union就必须加分号了,在主函数内不加分号就涉及到基础常识了——没有分号隔开怎能叫一句。
3.联合体union和大小端(big-endian、little-endian):
不过话说回来,某些情况下虽然不是很节约内存空间,但是union的复用性优势依然存在啊,比如方便多命名,这种“二义性”,从某些方面也可能是优势。这种方法还有个好处,就是某些寄存器或通道大小有限制的情况下,可以分多次搬运。
6.本质&进阶:
根据union固定首地址和union按最大需求开辟一段内存空间两个特征,可以发现,所有表面的定义都是虚的,所谓联合体union,就是在内存给你划了一个足够用的空间,至于你怎么玩~它不管~!(何止是union和struct,C不就是玩地址么,所以使用C灵活,也容易犯错)
没错,union的成员变量是相当于开辟了几个接口(即union包含的变量)!但是,没开辟就不能用了?当然也能用! 写个小测试:
一个例子了然,我的结构体只定义了int和double“接口”,只要我获得地址,往里边扔什么数据谁管得到?这就是C语言的强大,这就是union的本质——只管开辟一段空间。 有些东西,熟悉编译原理和编译器工作过程的话,解决会更容易点,虽然我现在这方面技能不太强,不过一般问题也足够分析了。 以上转自:https://www.cnblogs.com/tianlangshu/p/5204521.html. 结构体的嵌套问题结构体的自引用(self reference),就是在结构体内部,包含指向自身类型结构体的指针。 结构体的相互引用(mutual reference),就是说在多个结构体中,都包含指向其他结构体的指针。 1. 自引用结构体1.1 不使用typedef时 错误的方式: struct tag_1{ struct tag_1 A; int value; }; 这种声明是错误的,因为这种声明实际上是一个无限循环,成员A是一个结构体,A的内部还会有成员是结构体,依次下去,无线循环。在分配内存的时候,由于无限嵌套,也无法确定这个结构体的长度,所以这种方式是非法的。 正确的方式: (使用指针) struct tag_1{ struct tag_1 *A; int value; }; 由于指针的长度是确定的(在32位机器上指针长度为4),所以编译器能够确定该结构体的长度。 1.2 使用typedef 时 错误的方式: typedef struct { int value; NODE *link; } NODE; 这里的目的是使用typedef为结构体创建一个别名NODEP。但是这里是错误的,因为类型名的作用域是从语句的结尾开始,而在结构体内部是不能使用的,因为还没定义。 正确的方式:有三种,差别不大,使用哪种都可以。 typedef struct tag_1{ int value; struct tag_1 *link; } NODE; struct tag_2; typedef struct tag_2 NODE; struct tag_2{ int value; NODE *link; }; struct tag_3{ int value; struct tag_3 *link; }; typedef struct tag_3 NODE;
2. 相互引用 结构体错误的方式: 错误的原因和上面一样,这里类型B在定义之前 就被使用。 正确的方式:(使用“不完全声明”) struct tag_a{ struct tag_b *bp; int value; }; struct tag_b{ struct tag_a *ap; int value; }; typedef struct tag_a A; typedef struct tag_b B; struct tag_a; struct tag_b; typedef struct tag_a A; typedef struct tag_b B; struct tag_a{ struct tag_b *bp; int value; }; struct tag_b{ struct tag_a *ap; int value; };
嵌套结构体时应注意: 结构体的自引用中,如下这种情况是非法的 但很多时候,的确需要使用到自引用,有个技巧,如下: 这里有一种情况值得注意: 以上转自:http://www.cnblogs.com/renyuan/archive/2012/11/30/2796792.html. 下面有个测试小程序:(基本上涵盖了所有的情况)帮助你更好的理解结构体和联合体占用的内存大小.
输出结果:
|
|