自定义类型之结构体

一、基础知识

1.结构体的声明

1.1结构类型的声明

关键字 自定义标识符

{

类型名1 成员名表1;

......

类型名n 成员名表n;

};

例:


struct student
{
  long num;
  char name[20];
  float score;
};

1.2结构类型变量的声明

关键字 自定义标识符 结构变量

  • 直接声明


struct student//系统只为已声明变量的结构类型分配存储空间
{
  long num;
  char name[20];
}stu1,stu2;//此结构变量占字节数:4+20=24;
  • 间接声明


struct student
{
  long num;
  char name[20];
};
struct student stu1,stu2;//关键字  自定义标识符  结构变量
  • 无类型名声明(匿名结构体)//只用一次时


struct //省去结构名,直接给出结构变量
{
  long num;
  char name[20];
}stu1,stu2;//无结构类型名,无法再次声明该结构类型的变量

😵注意:即使匿名结构体成员相同,在编译器看来也是不同类型的结构体!


struct 
{
  long num;
  char name[20];
}x;
struct 
{
  long num;
  char name[20];
}*p;
p=&x;

1.3类型重命名(较简便)


typedef struct Node
{
  int data;
  long num;
}Node,*pNode;//pNode-->struct Node*(结构体指针重命名)
int main()
{
Node n;
return 0;
}

2.结构体的自引用

👀引:数据结构:线性/树形/图

线性:顺序表 链表(数据域|指针域)


错误示例:
struct Node
{
   int data;
   struct Node n;
};
int main()
{
 struct Node n;
 return 0;
}
正确示例:
struct Node
{
int data;//数据
struct Node* next;//指针
};
!注意:错误自引用
typedef struct 
{
  int data;
  Node* next;
}Node;
int main()
{
Node n;
return 0;
}
修正:
typedef struct Node
{
  int data;
  struct Node* next;
}Node;
int main()
{
Node n;
return 0;
}

3.结构变量的初始化


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

4.结构体传参(待补充)

二、😈结构体内存对齐

😇引例:


struct S1 //对齐数
{
char c1;  //1
int i;    //4
char c2;  //1   //最大对齐数为4   4的倍数为12
}s1;

struct S2
{
char c1;  //1
char c2;  //1
int i;    //4   //最大对齐数为4   4的倍数为8
}s2;

2.1结构体的对齐规则:

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

  1. 其他成员变量在对齐数整数倍的地址处。

对齐数 = 编译器默认对齐数 与 成员所占字节数 的较小值

(VS中默认对齐数为8/Linux环境无默认对齐数,自身大小即为对齐数)

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

s1

s2

内存

内存

偏移量

c1

c1

0

waste

c2

1

waste

waste

2

waste

waste

3

i

i

4

i

i

5

i

i

6

i

i

7

c2

8

waste

9

waste

10

waste

11

12

2.1.1练习:


struct S3
{          //偏移量(所放位置)
double d;  //0-7
char c;    //8
int i;     //12-15      //最大对齐数为8
}s3;//16
Sum:解决C语言结构体内存问题思路:写出各个变量所占字节数x,列出系统默认对齐数D,比较x与D,取最小值作为当前变量的对齐数z,在z的整数倍处存放相关z中最大值的倍数做结构体所占字节数。

2.1.2拓展:offsetof-宏

  1. 用途:计算结构体成员相对于起始位置的偏移量

  1. 原型:

#include<stddef.h>

size_t offsetof(structName,member name);


struct S3
{          //偏移量(所放位置)
double d;  //0-7
char c;    //8
int i;     //12-15      //最大对齐数为8
}s3;//16
printf("%u",offsetof(struct S3,d));//0  8  12

2.1.3嵌套结构体的内存对齐规则

嵌套的结构体对齐到自己最大对齐数的整数倍处,整体大小是所有最大对齐数(含嵌套结构体对齐数)的整数倍。


struct S3
{
double d;//8
char c;//1
int i;//4   //最大对齐数8
};
printf("%d\n", sizeof(struct S3));
struct S4
{
char c1;//1
struct S3 s3;//16
double d;//8         //所有对齐数最大为8
};
printf("%d\n", sizeof(struct S4));//32

偏移量

偏移量

偏移量

偏移量

偏移量

c1

0

waste

7

s3

14

s3

21

d

28

waste

1

s3

8

15

22

29

waste

2

9

16

23

30

waste

3

10

17

d

24

31

waste

4

11

18

25

32

waste

5

12

19

26

33

waste

6

13

20

27

34

2.2内存对齐的存在原因

1. 平台原因(移植原因):

不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特

定类型的数据,否则抛出硬件异常。

2. 性能原因:

数据结构(尤其是栈)应该尽可能地在自然边界上对齐。

原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访

问。引例引用:比特鹏哥的图片讲解板书

😷:Sum:结构体的内存对齐是拿空间来换取时间的做法。

改进:设计结构体的时候,既要满足对齐,又要节省空间,让占用空间小的成员尽量集中在一起。

2.3默认对齐数的修改

对齐方式不合适时可更改默认对齐数


struct S  字节 默认对齐数 对齐数  偏移量
{
char c;   //1    8       1       0
double d; //8    8       8      8-15
};//16
#pragma pack(4)//设置默认对齐数为4
struct S  字节 默认对齐数 对齐数  偏移量
{ 
char c;   //1    4       1        0
double d; //8    4       4      4-11
};//12
#pragma pack()//取消,还原为默认

三、位段(位指二进制位)

3.1什么是位段

位段的声明和结构类似,不同之处:

  • 1.位段的成员必须是 int、unsigned int 或signed int 。(char也可以)

  • 位段的成员名后边有一个冒号和一个数字(数字意义:该成员占用比特位数

例如:


struct A     //A就是一个位段类型
{
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};//47bit
printf("%d\n", sizeof(struct A));//8bite-->2*int

3.2位段的内存分配

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

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

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


struct S     //剩余比特位
{            //_a是int-4byte-32bit
int _a:2;   //30
int _b:5;   //25
int _c:10;  //10
int _d:30;  //开辟一个新的4byte-不知_b是否占用原字节剩余的15bit
};
//16位机器-16bit

详细图解(图片来自比特课件)

3.3位段的跨平台问题

  • 1. int 位段被当成有/无符号数不确定

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

  • 3. 位段中成员在内存中分配标准尚未定义(左-右||右-左)。

  • 4. 含两个位段的结构,第一个位段剩余位无法容纳第二个位段成员,剩余的位不确定舍弃||利用。

Sum:位段可以节省空间,但是存在跨平台的问题

3.4位段的应用

  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿猿收手吧!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值