结构体
结构体的的声明:
struct tag { member-list; //结构体成员列表 }variable-list; //变量列表
这里举例定义一个学生类型的结构体
struct Stu { //成员变量 char name[20]; int age; int weight; }s3,s4,s5;//也可以直接创建,这里是全局变量 int main () { struct Stu s1;//创建结构体变量 struct Stu s2;//这里是局部变量 return 0; }
匿名结构体类型
struct //结构体名,省略掉。 { char cl; int b; double d; }s1; struct { char cl; int b; double d; }*p; //结构体的指针变量
在这里,p = &s1;会报错,类型不兼容。其次建议不要使用该种写法,一次性用品结构体,无法创建新的结构体变量。
结构的自引用
在数据结构中,描述数据在内存的存储结构,顺序表,单链表都是线性结构,二叉树,树结构
当一个结构体要包含一个自己一样的结构体,
struct Node//可确定结构体大小 { int data; struct Node* next; };
这里只能用该结构体去包含自己的指针,不能直接在结构体在创建本身。
结构体变量的定义和初始化
struct S { int a; char c; }s1; struct B { float a; struct S s; }; int main() { int arr[10] = {1,2,3}; struct S s2 = { 100,'q' }; struct B sb = { 3.14f,{200,'w'} };//浮点数精确保存 printf("%f,%d,%c", sb.a, sb.s.c); return 0; }
定义指针类型的
struct S { char name[20]; int* ptr; }; int main() { struct S a = { "ABXDEF",NULL };//&+变量 return 0; }
结构体的大小
先举例
struct s1 { int a; char c; }; struct s2 { char c; int a; char d; }; struct s3 { int a; char c; char d; char e; }; int main() { printf("%d", sizeof(struct s1));//8 printf("%d", sizeof(struct s2));//12 printf("%d", sizeof(struct s3));//12 return 0; }
为什么会是这样呢?这里需要求了解关于结构体内存是对齐的知识。先了解结构体是怎样在内存中存放的。
存放规则 1.每一个字节相对起始位置都有一个偏移量,结构体的第一个成员永远被放在0偏移处,对齐数假设从0开始往后。
2.从第二个成员开始,以后每个成员都要对齐到某个对齐数的整数倍数。这个对齐数是:成员自生身大小和默认对齐数中的较小值 备注 vs环境下是8,gcc下没有,默认为自身大小为对齐数。
3.结构体的总大小必须是,所有成员对齐数中最大对其数的整数倍,如果不是,则浪费空间使对齐。
这里举例 struct s2
从这里可以看出,结构体成员的定义顺序会影响其在内存的存放方式
struct _s2 { char c; char d; int a; };
第四条规则:如果嵌套了结构体,则该结构体只能放在自身最大对其数的整数倍的位置上,整个结构体的大小必须是最大对齐数的整数倍(包含嵌套结构体中最大的对其数)。
struct s3 { int a; char c; char d; char e; struct s1 s; }; int main() { printf("%d", sizeof(struct s3));//16 return 0; }
设计结构体时,既要满足对齐,又要节省空间
这里提供了可以修改默认对齐数
#pragma pack(8);设置默认对齐数为8
offsetof宏,求该成员与上一个成员的偏移量.
结构体传参
void print2(const struct s* ps) { printf("%d", ps->num); }
结构体实现的位段
位段的声明和结构是类似的,位段的成员通常为int unsigned int,位段的成员名后边有一个和一个数字
位段表示的意义:给一定的大小空间,结构体成员认为是足够的。
struct A { int _a : 2;//2个比特 int _b : 5; int _c : 10; int _d : 30; }; int main() { struct A sa = { 0 }; printf("%d\n", sizeof(sa));//8个字节 return 0; }
不同的编译器,位段是不一样的。在vs中
struct S { char _a : 3; char _b : 4; char _c : 5; char _d : 4; }; int main() { struct S s = { 0 }; s._a = 10; s._b = 12; s._c = 3; s._d = 4; return 0; }
1.分配的内存中比特位是从右向左使用,
2.分配的内存空间不够成员使用时,浪费掉。
位段是不跨平台的位段的成员可以是 int 、unsigned int、 signed int 或者是 char (属于整型家族)类型
位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的。
位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
枚举
顾名思义。就是列举。把可能的取值一一列举。如星期几,性别等,枚举的用法简单,这里不做赘述。
枚举类型的定义
enum Day { Mon=1, Tuse, Thus, wentu,//枚举常量,可以赋值 frid, }; int main() { enum Day day1 = Mon; printf("%d\n", Mon);//打印直接打印 return 0; }
枚举可以使代码的可读性增加,便于调试。可定义多个常量。