结构体
类型声明
常规声明已在初阶结构体阐述,在这里再来分享一个特殊声明
:
这种声明也叫匿名结构体类型
1.该定义方式一般在只用一次的情况下应用
2.匿名结构体的成员如果一样,在编译器看来也是不同类型的结构体,如:
struct
{
char name[20];
char id[12];
}ss;
struct
{
char name[20];
char id[12];
}a[20],*ps;
int main()
{
ps = &ss;
return 0;
}
会报错,显示类型不兼容
自引用
在结构体中包含一个类型为该结构本身的成员,是否可以?
事实上,这种方法有误!!!
int data
数据区
struct Node next
指针域,改正struct Node* next
再来看一种:
错误点:类似于先鸡还是先有蛋的问题
(typedef是类型重命名)
再来一组:
typedef struct Node
{
int data;//数据
struct Node* next;//指针
} Node, * pNode;
//pNode相当于struct Node*
变量的定义和初始化
struct Book
{
char name[20];
float price;
char id[12];
}s = { "语文", 55.5f, "PGC001" };
struct Node
{
struct Book b;
struct Node* next;
};
int main()
{
struct Book s2 = { "数学", 66.6f, "HG001" };
struct Node n = { {"英语", 66.8, "TG001"}, NULL };
return 0;
}
内存对齐(重点!!!)
先看一个案列:
struct s1
{
char c1;//1
int i;//4
char c2;//1
};
struct s2
{
char c1;//1
char c2;//1
int i;//4
};
int main()
{
struct s1 s1;
struct s2 s2;
printf("s1:%d\n", sizeof(s1));//12
printf("s2:%d\n", sizeof(s2));//8
return 0;
}
运行结果:
首先,来看对齐规则:
补充:VS编译器默认对齐数为8;Linux环境下没有默认对齐数,没有默认对齐数时,自身大小就是对齐数。
练习一下:
struct s3
{
double c1;
char c2;
int i;
};
它的大小是多大呢?答案是:16。你做对了吗?若没有请 注意第二条规则的理解与使用!
验证偏移量的函数:
struct s3
{
double c1;
char c2;
int i;
};
#include<stddef.h>
//offsetof - 宏 包含于头文件 stddef.h
//计算结构体成员相对于起始位置的偏移量
int main()
{
printf("%d\n", offsetof(struct s3, c1));
printf("%d\n", offsetof(struct s3, c2));
printf("%d\n", offsetof(struct s3, i));
return 0;
}
在看一个例子,假如结构体中的成员中还有结构体呢?
struct s3
{
double c1;
char c2;
int i;
};
struct s4
{
char c1;
struct s3 s3;
double d;
};
int main()
{
struct s4 s;
printf("%d\n", sizeof(s));
return 0;
}
这里根据对齐规则第四条,s3先自身根据对齐规则确定大小,然后把s3作为一个普通成员变量来处理即可。
补充:s的最大对齐数位为8,不要当成16!!!
内存对齐的意义:
1.平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2.性能原因
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
总体来说: 结构体的内存对齐是拿空间来换取时间的做法。
为了尽量节省空间,应该将占用空间小的成员变量集中在一起·(如案例一)。
修改默认对齐数
(特殊点,当4改为1时,不对齐)
#pragma pack(4)//设置默认对齐数为4
struct s
{
char c;
double d;
};
#pragma pack()//取消设置
int main()
{
struct s s1;
printf("%d", sizeof(s1));
return 0;
}
结论:结构在对齐方式不合适的时候,可以自己更改默认对齐数。
传参
结构体传参,要传结构体的地址。
实现位段(位段的填充&可移植性)
还有一个·char。
struct A
{
int a : 2;
int b : 3;
int c : 3;
int d : 3;
};
位段内存分配
1.位段的成员可以是int unsigned int signed int或者是char(属于整形家族)类型
2位段的空间上是按照需要以4个字节( int)或者1个字节( char)的方式来开辟的。
3.位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
位段应用
枚举
定义
使用
enum Sex
{
male = 2,//枚举初始化
female,
secret
};
enum Day
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
int main()
{
enum Sex s = male;
enum Day d = Fri;
enum Day d1 = 4;
//在c++中该语法有误,d1 和 4 不是一种类型,不能赋值
//枚举具有类型检查
//避免命名污染,与其他地方冲突
printf("%d\n", s);
printf("%d\n", d);
printf("%d\n", d1);
return 0;
}
优点
联合(共用)
类型的定义
union Un
{
char c;
int i;
};//两个对象,同一时间只用一个
int main()
{
union Un u;
return 0;
}
特点
联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。
int check_sys()
{
union u
{
char c;
int i;
}u;
u.i = 1;
return u.c;
}
int main()
{
if (1 == check_sys())
{
printf("小");
}
else
printf("大");
return 0;
}
大小计算
需要对齐(最大对齐数的整数倍),如: