【C语言】结构体与位段

目录

一、结构体声明

1.1结构体基础知识

1.2 结构的声明

1.3 结构体自引用

1.4 结构体变量的定义和初始化

1.5 结构体内存对齐

结构体内存对齐出现的原因:

结构体内存对齐规则:

1.6 修改默认对齐数

小练习:offsetof 宏的实现

1.7 结构体传参

二、位段

2.1、位段的声明

2.2 位段的内存分配

2.3 位段的跨平台问题


一、结构体声明

1.1结构体基础知识

        结构体属于自定义类型,是用来描述复杂对象的,例如一个人的信息,需要各种不同类型的变量去描述它。


1.2 结构的声明

一般声明:

struct Student// 结构体标签
{
 char name[20];//名字  结构体成员
 int age;//年龄
 char sex[5];//性别
 char id[20];//学号
}(可以在此处创建结构体变量,属于全局变量,也可以省略); //分号不能丢

特殊声明:即可以省略结构体标签的不完全声明,也称作结构体匿名声明

struct    //省略了结构体标签
{
 int a;
 char b;
 float c;
}x;

小练习:

struct
{
 int a;
 char b;
 float c;
}x;
struct
{
 int a;
 char b;
 float c;
}a[20], *p;

p = &x ???

        不能这样做,因为匿名声明的结构体创建的实际上是两个不同的结构体类型。不能从结构体成员中判断是否相同,在编译器中,这两个结构体是不同类型,所以会报错。


1.3 结构体自引用

        结构体自引用是不能直接自己引用自己的结构体类型的,因为无法确定sizeof(结构体),会出现无限套娃的情况。所以需要创建一个自己结构体类型的指针,因为指针大小确定,通过指针作为成员变量实现结构体自引用。

struct Node
{
 int data;
 struct Node next;
};
//无法确定sizeof(struct Node),这样自引用不可行.

struct Node
{
 int data;
 struct Node* next;
};
//通过此结构体类型指针来进行自引用,sizeof(struct Node)为确定值,可行.

在自引用时,对结构体进行typedefine时要注意,不能将结构体成员中的struct省略掉。

typedef struct Node
{
 int data;
 struct Node* next;//此处struct不能省略,因为此处正在对结构体进行类型自定义的过程中,并没有完成。
}Node;


1.4 结构体变量的定义和初始化

//case1:
struct Point
{
 int x;
 int y;
}p1; //声明类型的同时定义变量p1;为全局变量
struct Point p2; //定义结构体变量p2;在主函数中创建为局部变量
struct Point p3 = {x, y};//初始化:定义变量的同时赋初值。

//case2:
struct Stu        //类型声明
{
 char name[15];//名字
 int age;      //年龄
}*p;(此处可不用创建结构体变量,可省略)
struct Stu s = {"zhangsan", 20};//初始化
struct Stu s = {.age=20, .name="zhangsan"};//自定义顺序初始化
printf("%s %d",s.name, s.age);//对结构体成员通过结构体变量访问
printf("%s %d",p->name, p->age);//对结构体成员通过结构体指针变量访问
printf("%s %d",(*p).name, (*p).age);//对结构体成员通过结构体指针解引用进行访问

//case3:
struct Node
{
 int data;
 struct Point p;
 struct Node* next; 
}n1 = {10, {4,5}, NULL}; //结构体嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化

结构体访问:

1. 通过结构体变量

1.通过结构体指针


1.5 结构体内存对齐

结构体内存对齐出现的原因:

        1、平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特 定类型的数据,否则抛出硬件异常。

        2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

        总结:结构体内存对齐目的是空间换时间,提升运行效率。

结构体内存对齐规则:

        1. 第一个成员在与结构体变量偏移量为0的地址处。

        2. 其他成员变量要对齐到(对齐数)的整数倍的地址处。对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。VS中默认的值为8

        3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。

        4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

在设计结构体时,需要将同类型的成员变量尽量连续放在一起。

【C语言】结构体与位段_第1张图片

 可以通过offsetof函数来查看每个成员变量相对于结构体类型指针的偏移量

#include 
printf("%d\n", offsetof(struct S2, c1));

1.6 修改默认对齐数

通过#pragma 来修改默认对其数以及恢复默认

#include 
#pragma pack(8)//设置默认对齐数为8
struct S1
{
 char c1;
 int i;
 char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认

小练习:offsetof 宏的实现

写一个宏,计算结构体中某变量相对于首地址的偏移,并给出说明,(在学习宏之后回来补充)


1.7 结构体传参

struct S
{
 int data[1000];
 int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参
void print1(struct S s)
{
 printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
 printf("%d\n", ps->num);
}
int main()
{
 print1(s);  //传结构体
 print2(&s); //传地址
 return 0;
}

        函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。 如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。(需下来参考函数栈帧)

        所以结构体传参的时候,要传结构体的地址。


二、位段

2.1、位段的声明

位段实际上是用时间换空间,使结构体空间尽可能的小,不需要对齐。

位段的声明和结构是类似的,有两个不同:

        1.位段的成员必须是 int、unsigned int 或signed int 。char也可以,属于整型家族。

        2.位段的成员名后边有一个冒号和一个数字。

struct A
{
 int _a:2; 
 int _b:5; 
 int _c:10; 
 int _d:30;
};

2.2 位段的内存分配

        1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型

        2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。

        3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。

struct A
{
 int _a:2; //先创建了一个int类型4个字节的空间,给int _a分配了2比特位的空间大小,还剩30位.
 int _b:5; //给int _b分配了5比特位的空间大小,还剩25位
 int _c:10; //给int _c分配了10比特位的空间大小,还剩15位
 int _d:30; //给int _d分配了30比特位的空间大小,因为前面不够30位,所以重新再开辟一个int类型空间,总共剩17位。
};

看看位段在vs2022中是如何开辟的

 【C语言】结构体与位段_第2张图片【C语言】结构体与位段_第3张图片

 但是这只是位段在vs中内存中开辟情况,在跨平台中还会有很多问题

2.3 位段的跨平台问题

        1. int 位段被当成有符号数还是无符号数是不确定的。

        2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。

        3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。

        4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。

2.4 位段实际应用

比如在网络中的数据传输,就需要位段的方式来存储数据,使得对数据存储的空间利用率最大,减少数据传输时的压力。

你可能感兴趣的:(C,c语言,c++,开发语言)