结构体
结构体其实就是值得集合和数组类似,但不同的是,数组所存放的元素必须是相同的类型。而集合可以存放不同类型的成员变量;除了内置类型外还可以存放结构体、指针、数组。
- 结构体的声明与定义以及初始化
- 结构体的声明使用struct关键字
//结构体的语法形式
struct tag{ //tag结构体标签
member-list; //成员列表
}variable-list; //变量列表
//描述一个学生
struct Student{
char name[20];
int age;
char sex[5];
char id[20];
}Stu;
- 特殊声明
声明结构体时也可以不完全声明
struct{
int a;
char b;
float c;
}x;
strcut{
int a;
char b;
float c;
}a[20], *p;
以上两个结构体省略了结构体标签tag,从成员列表来看两者是相同的;但是在编译器上会将两者视为两个不同的类型,所以以下操作为非法操作
p = &x; //非法操作
- 结构体的定义
//以下只是声明了结构体的类型
struct tag{
member-list;
};
//声明的同时定义
struct tag{
member-list;
}variable-list;
//第二种定义的方式
struct tag variable-list;
- 结构体变量的初始化
//定义变量的同时进行初始化
struct Student{
char name[20];
int age;
char sex[5];
char id[20];
}Stu ={"zhangsanfen", 18, "nan", 9527};
//第二种初始化方式
struct Student Stu ={"zhangsanfen", 18, "nan", 9527};
//结构体嵌套初始化
struct Point{
int x;
int y;
}p1;
struct Node{
int data;
struct Point p;
struct Node* next;
}p2 ={10, {8,9}, NULL};
struct Node p2= {10, {8,9}, NULL};
- 结构体成员访问
结构体成员的访问分为结构体变量访问( . )以及结构体指针访问( -> )两种方式。结构体访问成员的方式是由结构体所传递的参数所决定的,传递的是数值则使用点操作符访问;如果传递的是地址则使用结构体指针访问。
struct Stu{
char name[20];
int age;
};
void print(struct Stu* ps)
{
//使用点操作符访问结构体变量
printf("name = %s age = %d\n", (*ps).name, (*ps).age);
//使用结构体指针访问指向对象的成员
printf("name = %s age = %d\n", ps->name, ps->age);
}
int main()
{
struct Stu s = {"zhangsan", 20};
print(&s);//结构体地址传参
return 0;
}
- 结构体的自引用即在结构体中包含一个类型为该结构本身的成员
//错误自引用方式
struct Node{
int data;
struct Node next;
};
//正确自引用方式
struct Node{
int data;
struct Node* next;
};
- 结构体内存对齐
在了解结构体内存对齐之前,先了解一下偏移量和对齐数
。偏移量是指从结构体在内存中的首地址开始计算的量
。对齐数是编译器默认的对齐数与结构体成员所占内存大小的比较,并取其中的最小值
结构体内存对齐其实就是计算结构体站内存的大小
- 下图是struct s2在内存中的存放,按照结构体的内存对齐规则:
①char c1从0偏移量处开始存放;其他成员变量对齐到对齐数的整数倍,int i 的对齐数为4,应该从4的整数倍偏移量处存放;char c2的对齐数为1,所以存放在偏移量为8 的位置
② 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍,其中最大对齐数为4,此时的结构体大小为9 ,不是4的倍数只能向后取,取到偏移量为11处满足4的倍数要求,计算出此时结构体内存大小为12
- 下图为struct s1在内存中的存放,按照上面的规则得出此时的结构体所占内存大小为8
- 各个编译器所提供的默认对齐数是不同的,vs 所提供的对齐数是8但不是绝对的,可以通过" #pragrma pack ( )"修改
#pragma pack(8)//设置默认对齐数为8
struct S2
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
ps:如果想要尽可能节省内存,除非特殊要求,那么可以让占用空间小的成员尽量集中在一起
- 位段
按照百度的解释如下
位段(或称“位域”,Bit field)为一种数据结构,可以把数据以位的形式紧凑的储存,并允许程序员对此结构的位进行操作。这种数据结构的好处:
①可以使数据单元节省储存空间,当程序需要成千上万个数据单元时,这种方法就显得尤为重要。
②位段可以很方便的访问一个整数值的部分内容从而可以简化程序源代码。
而位域这种数据结构的缺点在于,其内存分配与内存对齐的实现方式依赖于具体的机器和系统,在不同的平台可能有不同的结果,这导致了位段在本质上是不可移植的
- 位段的声明和结构是类似的,有两个不同:
1.位段的成员必须是 int、unsigned int 或signed int 。
2.位段的成员名后边有一个冒号和一个数字。
比如:
struct A
{
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};
位段的大小计算方式如下:
printf("%d\n", sizeof(struct A));
- 位段的内存分配
- 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
- 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
- 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
位段仅限在结构体中使用
未完待续……