基础知识
(待补充)
typedef struct和struct(C和C++中的使用)
typedef struct OLNode { int i,j; int data; OLNode *right,*down; }OLNode,*OLink; //结构的对象OLNode, 指向结构的指针*OLink //可以这样写 struct OLNode{}; OLNode OLNode, *OLink;
总结3点
1.C语言中结构体struct的声明必须配合typedef,struct后跟的名字Name1称为标签,此时花括号后跟的名字Name2相当于类型别名:(struct Name1) == Name2;
2.C++中struct可单独声明新的结构体类型,此时struct后面跟的名字Name1是标签,可单独作为新类型名字使用来声明和初始化对象。若此时花括号后跟name2则相当于新声明的类型变量,可直接初始化,或后续初始化:Name1 name2;
3.C++中也可typedef 配合struct使用,此时与C语言相似。struct后面跟的Name1 是标签,花括号后面若有Name2则相当于新类型别名:(struct Name1) == Name1 == Name2。
具体分三块来讲述:
C和C++中struct的一般性声明
1. 在C中定义一个结构体类型要用typedef:
typedef struct Student { int a; } Stu; struct Student stu1; //OR Stu stu1; //如果没有typedef就必须用上面的struct Student stu1;来声明
这里的Stu实际上就是struct Student的别名:Stu == struct Student。另外这里也可以不写Student,于是也不能struct Student stu1;了,必须是Stu stu1;
typedef struct { int a; } Stu; Stu stu1;
2. C++里很简单,直接
struct Student { int a; }; Student stu2;
C++中struct与typdef struct声明的区别
struct Student { int a; } stu1; //stu1是一个变量
typedef struct Student2 { int a; } stu2; //stu2是一个结构体类型:stu2 == struct Student
//使用时可以直接访问: stu1.a = 10; //但是stu2则必须先: stu2 s2; //然后 s2.a = 10;
C语言中typedef struct的本质
typedef struct { int num; int age; } aaa, bbb, ccc;
观察编译器(VC6)的理解,这相当于
typedef struct { int num; int age; } aaa; typedef aaa bbb; typedef aaa ccc;
也就是说aaa, bbb, ccc三者都是结构体类型。声明变量时用任何一个都可以,在C++中也是如此。但是你要注意的是这个在C++中如果去掉了typedef关键字,那么aaa, bbb, ccc将是截然不同的三个对象。
typedef struct在C和C++中的区别
typedef struct tagMyStruct { int iNum; long lLength; } MyStruct;
上面的tagMyStruct是标识符,MyStruct是变量类型(相当于int,char等)。这语句实际上完成两个操作:
1) 定义一个新的结构类型
struct tagMyStruct { int iNum; long lLength; };
分析:tagMyStruct称为“tag”,即“标签”,实际上是一个临时名字,不论是否有typedefstruct 关键字和tagMyStruct一起,构成了这个结构类型,这个结构都存在。我们可以用struct tagMyStruct varName来定义变量,但要注意,使用tagMyStruct varName来定义变量是不对的,因为struct 和tagMyStruct合在一起才能表示一个结构类型。
2) typedef为这个新的结构起了一个别名,叫MyStruct。
typedef struct tagMyStruct MyStruct;
因此,MyStruct实际上相当于struct tagMyStruct,我们可以使用MyStruct varName来定义变量。
小结
同样的 typedef struct
在C中,这个声明后申请结构变量的方法有两种:
(1)struct tagMyStruct 变量名
(2)MyStruct 变量名
在c++中可以有
(1)struct tagMyStruct 变量名
(2)MyStruct 变量名
(3)tagMyStruct 变量名 //多出来的一种方式
深度解析结构体类型地址偏移量相关问题
结构体成员元素在结构体描述中的顺序会影响结构体使用时所需要的内存大小。
什么是偏移量
结构体变量中成员的地址和结构体变量地址之差。可以用offsetof(type, member)宏来确定成员的实际位置(定义于stddef.h),其中type是结构体类型,member是成员名。可知:第一个成员的偏移量为0,最后一个成员的偏移量加上最后一个成员的大小却不一定等于结构体大小。
什么是地址对齐
结构体成员是按照声明的顺序存储在连续的内存空间中,而结构体成员的类型不一定相同,系统在存储结构体时不是像数组一样将各个成员大小简单相加,而需要考虑地址对齐问题。
上述两种形式的结构体大小是否相同
不相等,虽然结构体成员相同,但是由于声明顺序不同导致地址偏移量不同。具体而言第一个结构体大小为16,第二个为24。
结构体存储时地址对齐,编译器需要遵循的原则
两条原则:
1.结构体变量中成员的偏移量必须是成员大小的整数倍;
2.结构体大小必须是所有成员大小的整数倍,即所有成员大小的公倍数。
对照上述两条原则,回过来看上述两个结构体大小就十分清楚了。Student1很好理解,Student2中scores成员偏移量为16,加上自身大小4后为20,由于20不是8的整数倍,取最小公倍数24为整个结构体大小。(这就是最后一个成员的偏移量加上最后一个成员的大小却不一定等于结构体大小)
增加点难度,再来看一个例子:
大家思考一下结构体Student3的大小?结构体ss偏移量?结构体ss大小为8,如果将ss当作一个整体Student3大小是不是24?
测试结果却是:
出现结构体嵌套时,怎么计算结构体大小
上述两个原则需要修改一下:
1.展开后的结构体的第一个成员的偏移量(嵌套的结构体偏移量)应当是被展开的结构体中最大成员的整数倍;
2.结构体大小必须是所有成员大小的整数倍,这里计算的是展开后的成员,而不是将嵌套的结构体看作一个整体。
总结与反思
了解结构体存储方式后,我们该怎么做才能充分利用内存,减少边界对齐带来的空间损失?
1.重排结构体成员声明列表,让那些对边界要求最严格(大)的成员首先出现,对边界要求最弱(小)的成员最后出现;
2.在考虑程序的可读性和可维护性的前提下,可以把相关联的成员放在一起而不用考虑列表顺序问题,否则参考1;
3.在程序声明几百个甚至几千个结构体时,减少内存的浪费要比程序的可读性更为急迫,这时可以通过增加注释来提高可读性。
tip:如何给结构体变量分配空间由编译器决定,以上代码均在linux/GCC下编译,windows/VC也是如此,其他平台可能有不同的处理方式。
(整理自网络)
参考资料