结构体,枚举,联合(未完待续)

1.结构体

结构体是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量
struct tag{
member-list
}variable-list;
例如描述学生

struct Stu{
	char name[20];
	int age;
	char sex[20];
	char id[20];
}s4,s5,s6;

struct Stu{
	char name[20];
	int id;
	char sex[20];
	char id[20];
};//注意要加分号
struct Stu s1;
struct Stu s2;

s4,s5,s6与s1,s2的区别在于s4,s5,s6为全局变量

特殊声明:

struct{
	int a;
	char b;
	float c;
}x;
struct{
	int a;
	char b;
	float c;
}*p;

//匿名结构体类型,匿名结构体一定为全局变量
p=&x;//类型不兼容,编译器会将上面两个声明当作完全不同的类型

结构体的自引用

struct Node{
int data;
struct Node*next;
};

typedef struct Node{
int data;
Node*next;
}Node;
//如果将struct后面的Node给省略掉,就会导致还未重命名Node就使用Node
这样用typedef重命名后,之后使用结构体变量 struct Node就可以直接用Node

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

struct stu{
char name[15];
int age;
};//定义
struct stu s1={“abcdef”,3};//初始化

struct stud{
int x;
char name[20];
struct stu;
};//结构体的嵌套定义
struct stud s b1={3,“abcdef”,{“cdeff”,20}};//结构体的嵌套初始化
在这里插入图片描述

2. 结构体内存对齐

规则:1.结构体的第一个成员永远放在0偏移处
2.从第二个成员开始,以后的每个成员都要对齐到某个对齐数的整数倍数(这个对齐数是:成员自身大小和默认对齐数的较小值)
在VS环境下,默认对齐数是8,gcc环境下,没有默认对齐数,对齐数就是成员大小
3.当成员全部存放进去后,结构体的总大小必须是所有成员的对齐数中最大对齐数的整数倍,如果不够,则浪费空间对齐
4.如果嵌套了结构体,那嵌套的结构体要对齐到自己结构体成员的最大对齐数的整数倍处,整个结构体的大小必须是最大对齐数的整数倍,最大对齐数包括嵌套结构体内部的成员变量

         struct s1{   
 char c1;
	int i;
	char c2;
};

该结构体大小为8

 struct s1{   
 char c1;
	char c2;
	int i;
};

该结构体大小为8

 struct s3{   
double d;
char c;
int i;
};

该结构体大小为16

struct s4{
	char c1;
	struct s3 b2;
	double b;
}

该结构体大小为32

为什么存在内存对齐
1.平台原因(移植原因)
不是所有的硬件平台都能访问任意地址上的任意数据的,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常
2.性能原因
数据结构(尤其是栈)应尽可能在自然边界上对齐
为了访问未对齐的内存,处理器需要作两次内存访问,而对齐的内存访问仅需要一次访问
struct s{
char c;
int a;
}s1;
如果是32位机器,一次读取4个字节,如果存在对齐则一次就可以访问,不存在就需要两次访问

总的来说,结构体的内存对齐是以空间换时间的做法
既要满足对齐,又要节省空间,让占用空间小的成员尽量聚在一起

修改默认对齐数
#pragma pack(8) 修改默认对齐数为8
#pragma pack() 恢复默认对齐数

例:#pragma pack(1)
struct s2{
char c1;
int i;
char c2;
};
s2结构体的大小为6

结构体传参
函数传参时,结构体是需要压栈的,会有时间与空间上的开销
如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销太大,会导致性能下降
所以结构体传参时,要传结构体地址,传结构体指针

2.位段

1.位段的成员可以是 int,unsigned int,signed int或者是char(属于整型类型家族) 类型
2.位段的空间上是按照需要以4个字节(int)或者1个字节(char)来开辟的
3.位段涉及很多不确定因素,位段是不跨平台的,应避免使用位段
不确定因素在于,位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义,位段中最大位的数目不能确定,16位机器最大16,32位机器最大32,写27在最大位为16的机器上会出问题)
4.位段的成员名后边有一个冒号和数字,冒号后面的数字代表分配给这个成员几个二进制位(vs环境,分配到的内存位从右向左使用,分配的内存剩余的比特位不够实用时,浪费掉)
请添加图片描述
注意10的二进制为1010,但分配给a的二进制位只有3位,故只会保留010
12只会保留1100

例题

#define MAX_SIZE A+B
struct _Record_Struct
{
   unsigned  char Env_Alarm_ID :4; //占第一个字节的前4位 
    unsigned  char Para1 :2; //占第一个字节的前第5、6位
    unsigned  char state; //占第二个字节的全部
    unsigned  char avail:1; //占第三个字节的前1位
}*Env_Alarm_Record;
struct _Record_Struct *pointer = (struct _Record_Struct*)malloc
(sizeof(struct _Record_Struct) * MAX_SIZE);

当A=2, B=3时,pointer分配(9 )个字节的空间
3*2+3=9

int main()
{
  unsigned char puc[4];
  struct tagPIM
  {
    unsigned char ucPim1;
    unsigned char ucData0 : 1;
    unsigned char ucData1 : 2;
    unsigned char ucData2 : 3;
  }*pstPimData;
  pstPimData = (struct tagPIM*)puc;
  memset(puc,0,4);
  pstPimData->ucPim1 = 2; 
  pstPimData->ucData0 = 3;
  pstPimData->ucData1 = 4;
  pstPimData->ucData2 = 5;
  printf(" %02x %02x %02x\n",puc[0], puc[1], puc[2], puc[3]);//%x以十六进制位打印
  return 0;
}

memset初始化后puc的内容为 0000 0000 0000 0000 0000 0000 0000 0000,每四个二进制位是一个十六进制位
ucPim1占用一个字节 0000 0010
ucData0占用1个字节,但存放3的二进制位为11所以只存入1
ucData1占用2个字节,但存放4的二进制位为100只存放00
ucData3占用3个字节,存放5的二进制位为101可以
所以第二个字节二进制位为
0010 1001
打印结果为 02 29 00 00

3.枚举

enum Day{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
枚举的可能取值,默认是从0开始递增的
enum Day 是枚举类型,{}中的内容是枚举类型的可能取值,也叫枚举常量
这些可能取值都是有初值的,默认从0开始,一次递增1,当然在定义时也可以赋初值
enum Day S=Mon;
enum Day S=2;//在c中允许,在c++中不允许

4.联合体(共用体)
联合是一种特殊的自定义类型
这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)
union un{
char c;
int i;
}//联合体的声明
union un U;//联合体的定义
sizeof(“%d”,sizeof(un));//4个字节

更改联合体其中一个内容时,别的内容也会更改,所以在同一时间只能使用一个内容

联合体的大小
1.联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小
2.当最大成员大小不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍

union Un1{
char c[5];
int i;
}   //大小为8
union Un2{
short c[7];
int i:
}   //大小为16

用联合体判断机器大小端

union un{
char c;
int i;
}
un.i=1;
if(un.c==1){
printf("大端");
}
else printf("小端");
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值