- 普通声明
- struct tag
- {
- member-list;
- }variable-list;
-
-
- 特殊声明
- 匿名结构体类型
- struct
- {
- int a;
- char b;
- float c;
- }x;
- struct
- {
- int a;
- char b;
- float c;
- }a[20], *p;
-
- 在上面代码的基础上,下面的代码合法吗?
- p = &x;
- 编译器会把上面的两个声明当成完全不同的两个类型。
- 所以是非法的
在声明结构的时候,可以不完全声明
比如:
// 匿名结构体类型struct{int a ;char b ;float c ;} x ;struct{int a ;char b ;float c ;} a [ 20 ], * p ;上面的两个结构在声明的时候省略掉了结构体标签( tag )。那么问题来了?// 在上面代码的基础上,下面的代码合法吗?p = & x ;警告:编译器会把上面的两个声明当成完全不同的两个类型。所以是非法的
在结构体中包含一个类型为该结构本身的成员是否可以呢?
// 代码 1struct Node{int data ;struct Node next ;};// 可行否?如果可以,那 sizeof ( struct Node ) 是多少?不可以,正确的自引用方式://代码 2struct Node{int data ;struct Node * next ;};
注意:
//代码3
typedef struct{int data ;Node * next ;} Node ;// 这样写代码,可行否?// 解决方案:typedef struct Node{int data ;struct Node * next ;} Node ;
这是一个特别热门的考点:结构体内存对齐
如何计算?
结构体的对齐规则:
1.第一个成员在与结构体变量偏移量为0的地址处。
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
对齐数 = 编译器默认的一个对齐数与该成员大小的较小值
注:vs中默认对齐数为8,若想改变对齐数,可使用#pragma pack()这个预处理指令修改,括号里写想设置的默认对齐数,如果单独#pragma,则是取消已经设置的默认对齐数,还原默认。Linux环境下没有默认对齐数,这时,它自身大小就是对齐数
3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
4.如果是嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
为什么存在内存对齐?有一些观点是这么说的:
1.平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常
2.性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要做两次内存访问;而对齐的内存访问仅需要一次访问
总的来说就是:结构体的内存对齐是拿空间换时间的做法
那么在设计结构体的时候,为了尽量满足对齐+节省空间,我们:
让占用空间小的成员尽量集中在一起
size_t offsetof( structName, memberName );
offsetof是一个宏函数,可以计算出结构体中某变量相对于首地址的偏移
需要包含的头文件:#include
printf("%u\n",offsetof(struct s1, c1) );
位段的声明和结构是类似的,有两个不同:1.位段的成员必须是 int、unsigned int 或signed int 。2.位段的成员名后边有一个冒号和一个数字。位段不对齐,因为设计它就是为了节省空间
比如:
struct A{int _a : 2 ;//00int _b : 5 ;//00000int _c : 10 ;//00 0000 0000int _d : 30 ;//00 0000 0000 0000 0000 0000 0000 0000};位段的位实际指的是二进制位,冒号后面的数字的意义是其所占内存的bit位。_a这个成员只占2个bit位,它组成的结构体所占的空间可能会小一点,可以节省空间
注意,这里位段的存储顺序和大小端无关,因为大小端是排字节序的,而这里存储的是一个字节为单位,内部比特位的存储,而不是字节的顺序
1. 位段的成员可以是 int、unsigned int、signed int 或者是 char (属于整形家族)类型2. 位段的空间上是按照需要以 4 个字节( int )或者 1 个字节( char )的方式来开辟的。3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段
// 一个例子struct S{char a : 3 ;char b : 4 ;char c : 5 ;char d : 4 ;};struct S s = { 0 };s . a = 10 ;s . b = 12 ;s . c = 3 ;s . d = 4 ;// 空间是如何开辟的?
1.int位段被当成有符号数还是无符号数是不确定的
2.位段中最大位的数目不能确定(16位机器最大16,32位机器最大32,写成27的话,在16会机器上会出问题)
3.位段中的成员在内存中从左向右分配,还是从右向左分配没有标准来定义
4.当一个结构包含两个位段,第二个位段成员比较大,无法容纳第一个位段剩余的位时,是舍弃剩余的位还是利用剩余的位,是不确定的
在某些情况下,会涉及到位段。数据在网络上传输时,会有数据的封装,注意,下图的源ip地址指的是发送数据(A)的ip地址,目的ip地址是接受数据(B)的ip地址
在网络协议栈会有下图这些,这些成员就能用位段的形式实现,本身空间就小,又节省了空间