C语言进阶整理 自定义类型:结构体 位段
文章目录
正片开始
一、结构体
结构是一些值得集合,这些值称为成员变量。结构的每个成员可以是不同类型。
数组是一组相同类型的元素集合
1.1 结构体的基本声明
struct tag
{
//成员变量;
};//分号别丢
例如
struct book
{
char name[20];
int price;
char id[12];
}b4,b5,b6;//是全局变量,也是结构体类型变量,相当于inta中的a
int main()
{
struct book b1={
0};//也可以这么设变量
}
1.2 结构体的特殊声明
在声明结构的时候,可以不完全声明。
//匿名结构体类型 无结构体标签
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}* p;
//这个问题合法吗?
p=&x;
//答案是不合法,因为编译器会当做两个类型,会出警告
1.3 结构体的自引用
在结构中包含一个类型为该结构本身的成员是否可以?
struct Node//Node 为节点,数据结构
{
int data;//4
struct Node next;//4+
};
答案是不可以,因为在struct Node类型中int为4个字节,而结构体struct Node nest中会反复出现在4+和反复调用自己,无限套娃,出现死递归。
解决办法:
//应该加上指针,找到下一个对应的地址
struct Node
{
int data;
struct Node* next;
};
注意:
typedef-- - 类型重定义
typedef struct
{
int data;
Node* next;
}Node;
//这样写代码,可行否?
答案是不行,想用Node指针定义next这个成员必须先定义Node,存在先后顺序
解决方法:
typedef struct Node
{
int data;
struct Node* next;
}Node;
1.4 结构体内存对齐
现在深入讨论一下计算结构体的大小,这节特别重要!!!
struct s1
{
double d;
char c;
int i;
};
printf("%d",sizeof(struct s1));
很多人觉得double 8字节,char1个字节,int4个字节,所以内存大小为13,其实大错特错,现在就来掌握下结构体的对齐规则。
下面看看编译器:
答案正确,确实是16.
嵌套情况尼?
struct s1
{
double d;
char c;
int i;
};
printf("%d\n",sizeof(struct s1));
struct s2
{
char c1;
struct s1 s;
double d;
};
printf("%d\n",sizeof(struct s2));
看看编译器:
答案正确,确实是32.
为什么存在内存对齐?
大部分参考资料都是这样说的
-
平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。 -
性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
总体来说:
结构体的内存对齐是拿空间来换取时间的做法。
那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:
那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:
//例如:
struct S1
{
char c1;
int i;
char c2;
};
struct S2
{
char c1;
char c2;
int i;
};
//S1和S2类型的成员一模一样,但是S1和S2所占空间的大小有了一些区别
1.5 修改默认对齐数
一般vs默认对齐数是8,那我们可以改变对齐数吗?
#pragma 这个预处理指令,可以改变我们的默认对齐数。
#pragma pack(4)//设置默认对齐数4
、、、、、、、、
#pragma pack()//取消设置的默认对齐数,还原默认
结论
结构在对齐方式不合适的时候,我么可以自己更改默认对齐数。
offsetof
这个是一个宏,返回的是: 结构体成员 在内存中的偏移量。
1.6 结构体传参
结构体有两种传参的方式:1.值传参 2、地址传参
哪一种比较好尼?还是看例子吧
结论:
结构体传参的时候,要传结构体的地址。
二、位段
2.1什么是位段
位段的声明和结构是类似的,有两个不同:
1.位段的成员必须是 int、unsigned int 或signed int ,(char)整型家族
2.位段的成员名后边有一个冒号和一个数字。
A就是一个位段类型。
那位段A的大小是多少?
struct A
{
int _a : 2;//_a 成员占2个bit位
int _b : 5;//_b 成员占5个bit位
int _c : 10;//_c 成员占10个bit位
int _d : 30;//_b 成员占30个bit位
};
2.2 位段的内存分配
-
位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
-
位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
-
位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
//一个例子 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; //空间是如何开辟的?
2.3位段的跨平台问题
int 位段被当成有符号数还是无符号数是不确定的。
位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机
器会出问题。位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。
总结:
跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。