自定义类型:结构体
结构体类型的声明
回顾一下结构体:结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量
1.结构体的特殊声明
声明结构体的时候也可以不完全声明
匿名结构体类型只能使用一次,后期不能使用这个类型在创建变量
c
#include<stdio.h>
//匿名结构体类型
struct
{
int a;
char b;
float c;
}x, y, z;
//编译器会把这两个类型当成完全不同的类型,会报错
// struct
//{
// int a;
// char b;
// float c;
//}*ps;
int main()
{
ps = &x;
return 0;
}
2.结构体的自引用
c
struct Node
{
int data;
struct Node* next;
};
定义结构体的时候不要使用匿名结构体。
对结构体重命名,简化后续创建变量:
c
#include<stdio.h>
typedef struct Node
{
int data;
struct Node* next;
}Node;
int main()
{
Node n;
return 0;
}
结构体内存对齐
首先我们来看一段程序
这里引入C语言库函数提供的一个宏,需要包含头文件<stddef.h>,用于返回偏移量。
结合上面的偏移量,我们画一个图来理解:
那么剩下的3个字节位置是不是就白白浪费了,这就涉及到了结构体的内存对齐
1.对齐规则
- 结构体的第1个成员对齐到和结构体变量起始位置偏移量为0的地址处。
- 从第2个成员变量开始,都要对齐到某个对齐数 的整数倍的地址处
对齐数=编译器默认的一个对齐数与该成员变量大小的较小值 。
VS默认的对齐数为8;Linux中gcc没有默认对齐数,对齐数就是成员自身的大小。- 结构体总大小为最大对齐数(结构体中每个成员变量都有一个对齐数,所有对齐数中最大的)的整数倍
- 如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对其书的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。
现在重新拿出刚刚那个例子就比较好理解了:
2.为什么存在内存对齐
由于某种平台原因以及性能原因,结构体的对齐是拿空间 来换取时间 的做法。那在设计结构体的时候,既要满足对齐又要节省空间又该如何做呢------如果没有特别要求成员变量顺序的话,尽量让占用空间小的成员尽量集中在一起
3.修改默认对齐数
**# pragma pack()**这个预处理指令,可以改变编译器的默认对齐数------设置的一般都是2的次方数.
结构体传参
c
#include<stdio.h>
struct S
{
int data[1000];
int num;
};
struct S s = { {1,2,3,4},1000 };
//结构体传参
void print1(struct S t)
{
printf("%s\n", t.num);
}
//结构体地址传参
void print2(struct S* ps)
{
printf("%d\n", ps->num);
}
int main()
{
print1(s);//传结构体
//传值调用
//浪费了时间和空间
print2(&s);//传地址
//传址调用
//效率高
return 0;
}

函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销;如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降。
因此:结构体传参的时候,要传结构体的地址
结构体实现位段
1.什么是位段
位段的声明和结构是类似的,有两个不同:
- 1.位段的成员必须是int,unsigned int或者signed int,在C99中位段成员的类型也可以选择其他整型家族类型,比如char。
- 2.位段的成员名后边有一个冒号和一个数字。
例:下面的A就是一个位段类型
c
struct A
{
int a:2;//这个数字表示该成员变量所占bit位
int b:5;
int c:10;
int d:30;
};
struct B
{
int a;
int b;
int c;
int d;
};
int main()
{
printf("%zu\n",sizeof(struct A));//8
//明显节省了空间
printf("%zu\n",sizeof(struct B));//16
return 0;
}
上面的结构体A共占47个bit位,6个字节就够了,但最终打印结果其占8个字节,这就涉及到位段的内存分配了
2.位段的内存分配
一个字节(整型)的内存中,到底是从左向右使用,还是从右向左使用不确定;
剩余的空间不能满足下一个成员时,是否浪费也不确定。
由上可知,在VS上从右向左使用是正确的;且在VS上浪费也是正确的。
- 位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的
- 位段涉及很多不确定因素,位段是不跨平台的,注重可移植性的程序应该避免使用位段
3.位段的跨平台问题
- int位段被当成有符号数还是无符号数是不确定的;
- 位段中最大位数的数目不能确定,16位机器最大16,32位机器最大32,写成27,在16位机器会出问题;
- 位段中的成员在内存中从左向右分配,还是从右向左分配,标准尚未定义;
- 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。
因而,跟结构相比,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在
4.位段的应用
在网络协议中,IP数据报的格式,我们可看到其中很多属性只用bit位就能描述,这里使用位段,能够实现想要的效果,也节省了空间,这样网络传输的数据报大小也会较小一些,对网络的畅通有帮助
5.位段使用的注意事项
位段的几个成员共有同一个字节,这些有些成员的起始位置并不是某个字节的起始位置,那么这些位置处是没有地址的。内存中每个字节分配一个地址,一个字节内部的bit位是没有地址的。
所以不能对位段的成员使用&操作符 ,这样就不能使用scanf直接给位段的成员输入值,只能是先输入一个变量中,然后赋值给位段的成员
本次内容到这里就结束了,谢谢观看!