结构体
数组是一组相同类型的元素的集合
结构体也是一些值的集合,结构的每个成员可以是不同类型
声明
struct Book
{
char name[20];
int price;
char id[12];
}b4,b5,b6; //全局变量
特殊声明
//匿名结构体类型
struct
{
char c;
int i;
char ch;
double d;
}s;
struct
{
char c;
int i;
char ch;
double d;
}*ps;
int main()
{
ps = &s;
return 0;
}
//编译器会把上面两个自定义结构体类型当成两个完全不同的类型
//所以这是非法的
int main()
{
//b1,b2,b3是局部变量2
int a;
struct Book b1;
struct Book b2;
struct Book b3;
return 0;
}
结构体自引用
//嵌套引用
struct A
{
int i;
char c;
};
struct B
{
int i;
struct A c;
};
//错误的自引用格式
struct N
{
int d;
struct N n; //err
};
//正确的自引用格式
struct Node
{
int data;
struct Node* next; //链表
};
typedef struct Node
{
int data;
struct Node* next;
}Node;
结构体变量的定义和初始化
struct stu
{
int age;
char i;
};
struct s
{
int c;
struct stu b;
};
int main()
{
struct s d = { 18,{18,'a'} };
printf("%d %d %c", d.c, d.b.age, d.b.i);
}
结构体内存对齐
1.结构体的第一个成员,放在结构体变量在内存中存储位置的0偏移处开始
2.从第二个成员往后的所有的成员都放在一个对齐数(成员的大小和默认对齐数的较小值)的整数倍的地址处
3.结构体的总大小是所有结构体成员的对齐数中最大的那个对齐数的整数倍让空间小的变量集中在一起就可以节约一部分空间 修改默认对齐数也可以节约空间
默认对齐数是可以被修改的
把默认对齐数改为2
#pragma pack(2)
struct s
{
char c1;
int i;
char c2;
};
#pragma pack(1) //一般设置2的几次方
struct s
{
char c1;
int i;
char c2;
};
#pragma pack() //取消掉之前设置的默认对齐数2,只有中间的成员默认对齐数为2
int main()
{
printf("%zd\n", sizeof(struct s));
return 0;
}
//宏 offsetof 求相对起始位置的偏移量
#include<stddef.h>
struct s
{
char c1;
int i;
char c2;
};
int main()
{
printf("%d\n", offsetof(struct s,c1));
printf("%d\n", offsetof(struct s,i));
printf("%d\n", offsetof(struct s,c2));
return 0;
}
结构体传参
结构体传参的时候,要传结构体的地址
位段
什么是位段?
1.位段的成员必须是int、unsigned int或signed int。
2.位段的成员名后边有一个冒号和一个数字。 位段 47 32+15
struct A
{
int _a : 2; //_a成员占2个比特位
int _b : 5; //_b成员占5个比特位
int _c : 10; //_c成员占10个比特位
int _d : 30; //_d成员占30个比特位
};
位段的内存分配
1.位段的成员可以是int、unsigned int、signed int或者是char(属于整型家族)类型
2.位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的。
3.位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段
struct S
{
char a : 3;
char b : 4;
char c : 5;
char d : 4;
};
不跨平台
1.int位段被当成有符号数还是无符号数是不确定的
2.位段中最大位的数目不能确定。(16位机器最大是16,32位机器最大是32,写成27,在16位机器 会出问题)。
3.位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4.当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余 的位还是利用,这是不确定的。 总结 跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在
int main()
{
struct S s = { 0 };
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
return 0;
}
枚举
//枚举顾名思义就是——列举。
//把可能的取值——列举。
//枚举类型的定义
enum Color
{
RED=5,//5 //赋初值
GREEN, //6
BLUE //7 //自增1
};
//枚举类型的可能取值
//常量
int main()
{
//enum Color c = BLUE;
printf("%d\n", RED); //0 //5
printf("%d\n", GREEN); //1 //6
printf("%d\n", BLUE); //2 //7
printf("%zd\n", sizeof(RED)); //4
return 0;
}
为什么使用枚举?
我们可以使用#define定义常量,为什么非要使用枚举?枚举的优点:
1.增加代码的可读性和可维护性
2.和#define定义的标识符比较枚举有类型检查,更加严谨。
3.防止了命名污染(封装)
4.便于调试
5.使用方便,一次可以定义多个常量。
int input = 0;
//枚举类型的使用
enum Option
{
exit,
mul,
sub
};
int main()
{
switch (input)
{
case exit:
break;
case mul:
break;
case sub:
break;
}
}