一、结构体:一些不同类型的值的集合,这些值称为结构体的成员变量。
1、结构体的创建
//创建一个简单的结构体:
struct stu{ //类型声明,结构体的标签
char name[20]; //成员1,姓名
int age; //成员2,年龄
char sex[3]; //成员3,性别
char id[20]; //成员4,学号
}; //末尾的分号不可以丢掉
2.结构体初始化
struct stu1{
char name[20];
int age;
};
struct stu1={"Walter",22}; //对stu1的变量同时赋初值(初始化)
3.结构体内存对齐(熟记四条规则)
- 1.第一个成员在与结构体变量偏移量为0的地址处;
-
2.其他成员变量要对齐到(对齐数)的整数倍地址处;
-
3.结构体的总大小为成员里最大对齐数的整数倍;
- 4.如果有结构体的自引用情况,还是对齐到最大对齐数的整数倍,结构体大小就是所有最大对齐数的整数倍。
画个图解释一下:
同样我们可以用一个预处理指令来修改默认对齐数,如下:
#pragma pack(8) //设置默认对齐数为8;
#pragma pack() //设置对齐数还原为默认;
4.结构体传参
struct s1{
int data[1000];
int num;
};
struct s1 s={{1,2,3,4},50};
//结构体传参数
void printf1(struct s1 s){
printf("%d\n",s.num);
}
//结构体地址传参数
void printf2(struct s1* ps){
printf("%d\n",ps->num);
}
int main(){
printf1(s);
printf2(&s);
return 0;
}
首选printf2函数,因为函数传参的时候,参数是需要压栈的,如果直接传结构体会导致系统开销较大,因此效率和性能大大降低。
结构体传参的时候,要传结构体的地址!
4.位段
1)位段的成员必须是int、unsigned int、signed int。
2)位段的成员名后面有一个冒号和一个数字。
struct A{
int _a:2;
int _b:4;
};
位段的大小在不同的编译器里是不一样的,所以研究位段的内存布局没有意义!
二、枚举:把所有可能的数值一一列举,在C中与int有着等价效果。
enum sex{ //enum sex 是枚举类型
Male; //枚举常量,默认初始值为0
Female; //枚举常量,默认初始值为1
Unkonw; //枚举常量,默认初始值为2
};
三、联合(共用体):一系列成员共用同一块空间的集合体
由于联合中各成员共用同一段空间,所以联合长度至少可以容纳最大的成员。
但是当最大成员大小不是 最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍。
干货,一道笔试题:利用union编写程序判断CPU是big-endian还是litter-endian
#include<stdio.h>
int main()
{
union Test
{
unsigned int a;
unsigned char b;
}tmp={0x12345678};
//如果打印出的是0x78,表明是litter-endian
//如果打印出的是0x12,表明是big-endian
if(tmp.b==0x78)
printf("The endian of cpu is litter-endian.\n");
else
printf("The endian of cpu is big-endian.\n");
return 0;
}