结构体
为什么要创建结构体类型?在我们处理复杂对象的时候,比如描述一个人的时候,它有名字,性别,身高,体重等一些方面的特征。用结构体打包描述的时候就比较方便。
结构体类型的声明
结构体类型的关键字struct
。
声明的基本模板为:
struct 标签
{成员;
}变量;结构体的成员可以是不同的类型。
结构体类型的特殊声明:
匿名结构体类型,它只能使用一次。
struct { int a; char b; }x; struct { int a; char b; }*p;
p=&x
这样写是错误的,在编译器看来,它们俩是不同的类型。
看下面这两种:
struct { int a; int b; }x; 这里的x是一个结构体类型的全局变量 typedef struct { int a; int b; }x; 这里的x是结构体类型。 typedef struct stu { int a; int b; }*put,stu; 这里的put等价于struct stu*类型 stu等价于struct stu类型
结构的自引用
例如:
typedef struct node { int x; struct node* p; }node; 这个自引用就是正确的。 typedef struct node { int x; node* p; }node; 这种就是错误的,这个问题的出现就和先有鸡还是先有蛋的问题一样。 不能知道是先重命名了还是先创建的。
结构体变量的定义和初始化
- 结构体变量的定义
struct student { char name[20]; int age; }a,b; //这种是一边声明,一边定义变量的,这里的变量a,b为全局变量 struct student c; //这种定义的变量是全局变量 int main() { struct student d; //这里的d为局部变量 return 0; }
- 简单的初始一下:(应该没有难道吧)
struct student { char name[20]; int age; }a={"zhangshan",20}, b={"wuanwu",18}; struct student c={"hh",0}; int main() { struct student d={"sb",88}; return 0; }
🟥结构体内存对齐
如何计算结构体的大小,就需要知道它在内存中是如何储存的。
而结构体在内存中存在结构体对齐的现象。
1.第一个成员变量放在偏移量为0的位置
2.后面的成员放在偏移量为对齐数的整数倍的位置。
3.对齐数:编译器默认的一个对齐数与成员大小的较小值
vs的默认对齐数位8
4.结构体的总大小为每个成员默认最大对齐数的整数倍。
5.如果含有结构体嵌套的情况,镶嵌的那个结构体的对齐数是里面成员的最大对齐数。
- 下面仔细讲解一下:
struct S1 { char c1; int i; char c2; }; printf("%d\n", sizeof(struct S1)); struct S2 { char c1; char c2; int i; }; printf("%d\n", sizeof(struct S2)); struct S3 { double d; char c; int i; }; printf("%d\n", sizeof(struct S3)); struct S4 { char c1; struct S3 s3; double d; }; printf("%d\n", sizeof(struct S4));
按上面的分析:可知结构体的大小分别为:12,8,16,32
运行看一下情况:
修改默认对齐数
我们用
pragma
修改默认对齐数
例子:
#pragma pack(4) //这里面的数字表示的是默认对齐数 struct p { int a; int b; char c; }; #pragma pack()
结构体传参
在结构体传参的时候,最好选择传址调用,有两个好处
1.可以减少对空间的浪费
2.可以对里面的数据进行修改
简单的例子:
#include <stdio.h> struct student { char name[20]; int age; }; void f(struct student* p) { } int main() { struct student p; f(&p); return 0; }
结构体实现位段
位段的实现和结构体类似,只不过位段的成员的类型只能是
unsigned int
或者int
类型,char
类型的也可以。每个成员名后面要加上
:和数字
举个简单的例子:
struct stu { int a : 4; int b : 2; };
后面的数字表示bite位。位段不存在对齐。
位段不具有跨平台性:
1.位段中没有规定在内存使用的过程中,是从左使用还是从右使用。
2.不能满足下一个成员使用的空间是舍弃还是保留的问题没有规定。
3.int
位段中无符号还是有符号的问题没有规定
结构体实现位段的内存分配
struct S { char a : 3; char b : 4; char c : 5; char d : 4; }; struct S s = { 0 }; s.a = 10; s.b = 12; s.c = 3; s.d = 4;
枚举
枚举类型的关键字为
enum
,枚举就是把所有的可能列举出来。里面的叫做枚举常量。它们也是有值的,如果没有给他们初始化。默认第一个是0,后面的依次增加。
例子:
enum colour { //注意后面是逗号, red,//值是0 yellow,//值是1 green//值是2 }; enum colour { red=2,//这种不是赋值,而是给这个常量一个初始值。 yellow=5, green//它的值为6 };
联合(共用体)
联合类型的定义
关键字
union
。里面的成员都是占用同一块空间。
例子:
union people { char a; int b; };
联合大小的计算
联合体可能是最大类型所占空间的大小。
当结构体的大小不是最大对齐数的整数倍时,需要对齐。
例子:
union Un1 { char c[5]; //开辟了5个字节的空间 int i; //i占4个大小的空间,开辟的空间够用 //共5个字节的空间,但是不是4的整数倍,存在内存对齐, //最终为8个字节的大小 }; union Un2 { short c[7]; int i; //开辟的14个字节的空间也不是4的整数倍,需要对齐。 //最终的大小为16个字节 }; int main() { printf("%d\n", sizeof(union Un1)); printf("%d\n", sizeof(union Un2)); return 0; }
看结果: