目录
一、结构体
结构体:由变量组成的数据类型。
结构体声明
//创建「人」的数据类型
struct People //「struct People」为数据类型名,其中「struct」固定,「People」自定义
{
char name[10];//姓名
int age;//年龄
};
特殊声明(不加结构体标签)
struct
{
char name[10];
int age;
};
当用特殊声明创建多个结构体时,编译器将全部视为不同类型。
例:用特殊声明创建 2 个一样的结构体,创建变量后进行赋值。
结构体创建变量
(1)主函数内创建变量(局部变量)
struct People p1, p2;
struct People* p1, *p2;//指针变量
(2)声明结构体时创建变量(全局变量)
struct People
{
char name[10];
int age;
}p1,p2;//创建「p1」「p2」两个「struct People」类型的变量
struct People
{
char name[10];
int age;
}*p1,*p2;//创建「p1」「p2」两个「struct People」类型的指针变量
结构体自引用
错误:
struct People
{
char name[10];
int age;
struct People p;
};
当 People 中创建了 p 时,p 里面还会包含一个 p,不断循环下去,导致计算不出占用内存大小,所以编译器报错。
正确:
struct People
{
char name[10];
int age;
struct People *p;
};
当用指针的方式进行自引用,只需提供一个固定的指针类型大小就可以了。
特殊声明不可进行自引用
将 struct 进行重定义之后,编译器还是会将创建出来的不同变量视为不同类型。
结构体重定义
typedef struct People
{
char name[10];
int age;
}P;
//将「struct People」重定义为「P」
结构体变量初始化
(1)一般初始化
struct People
{
char name[10];
int age;
};
struct People p = { "张三",18 };
或
struct People
{
char name[10];
int age;
}p = { "张三",18 };
(2)嵌套初始化
struct A
{
int b;
};
struct People
{
char name[10];
int age;
struct A a;
};
struct People p = { "张三",18 ,{0} };
//因为p中的a是结构体,所以也需要使用{}
结构体内存对齐
用空间换取时间
作用:
(1)提高可移植性,某些平台只能在特定地址取出特定数据类型。
(2)提高性能:有时访问未对齐内存可能要比对齐内存多访问一次,因为内存对齐后,系统就知道从哪开始访问获取该数据。
内存对齐规则:
(1)所有成员须对其到对齐数的整数倍偏移量处
(2)结构体总大小为成员最大对齐数的整数倍
(3)数组或嵌套结构体的偏移量为自身内变量的最大偏移量
对齐数:该成员占用字节数,VS 下最大不超过8
偏移量:内存单位与起始位置的距离
集中占用空间小的成员可以节约空间
例 1:
struct X
{
char c1;
int i;
char c2;
}x;
struct Y
{
char c1;
char c2;
int i;
}y;
int main()
{
sizeof(x);
sizeof(y);
return 0;
}
答案:12、8
例 2:
struct X
{
char c1;
int i;
char c2;
};
struct Y
{
char c1;
char c2;
struct X x;
char c3;
}y;
int main()
{
sizeof(y);
return 0;
}
答案:20
offsetof
使用 offsetof 可查看结构体类型中变量所在偏移处,需包含头文件 < stddef.h>
例:
struct X
{
char c1;
int i;
char c2;
}x;
struct Y
{
char c1;
char c2;
struct X x;
char c3;
}y;
int main()
{
printf("%d\n", sizeof(y));
printf("%d\n", offsetof(struct Y, c1));
printf("%d\n", offsetof(struct Y, c2));
printf("%d\n", offsetof(struct Y, x));
printf("%d\n", offsetof(struct Y, c3));
return 0;
}
修改最大对齐数
例:
#pragma pack(1)//将最大对齐数改为1
struct X
{
char c1;
int i;
char c2;
}x;
#pragma pack()//将最大对齐数改回默认值
结构体传参
(1)值传递
struct A
{
int i;
};
void print(struct A a)
{
printf("%d", a.i);
}
int main()
{
struct A a = { 1 };
print(a);
return 0;
}
(2)地址传递
struct A
{
int i;
};
void print(struct A* a)
{
printf("%d", a->i);
}
int main()
{
struct A a = { 1 };
print(&a);
return 0;
}
函数传参时需要进行压栈,如果参数过大会导致不必要的时间、空间消耗,所以结构体传参时,地址传递优于值传递。
二、位段
声明结构体时,在其中的成员后加上为该成员分配的内存(以位为单位),该结构体成员称为位段。
例:
struct S
{
int a : 3;//给a分配3个比特位
int b : 4;//给b分配4个比特位
int c : 5;//给c分配5个比特位
int d : 4;//给d分配4个比特位
};
位段的内存分配
(1)位段只能是整型
(2)位段以不同整型大小按需开辟空间
例:
struct S
{
int a : 3;
int b : 4;
int c : 5;
int d : 4;
};
int main()
{
struct S s = { 0 };
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
return 0;
}
位段的跨平台问题
位段不可跨平台
(1)int 类型被当做有符号或无符号不确定
(2)位数不确定(16 为机器下 int 为 2 字节)
(3)内存从左向右分配或相反,尚未确定
(4)开辟新空间时,旧空间利用或舍弃未确定
位段的应用
位段应用在数据报文中,因为报文中每个字段所需空间极小。
三、联合
所有成员共用同一片空间的数据类型叫联合。
联合的声明、变量创建同结构体一样,只需将结构体中的「struct」替换为「union」
联合内成员的空间占用
例:
union U
{
int i;
char c;
}u;
int main()
{
printf("%d\n", &(u.i));
printf("%d\n", &(u.c));//(1)
u.i = 0x11223344;
u.c = 0x55;
printf("%x\n", u.i);//(2)(小端环境下)
return 0;
}
结果:
(1)2 个地址相同
(2)44332200
联合的大小计算
联合的大小为最大成员大小且为成员内最大对齐数的倍数
例 1:
union U
{
char c[5];
int i;
}u;
int main()
{
printf("%d\n", sizeof(u));
return 0;
}
答案:8
最大成员大小为 5(char [5]),最大对齐数为 4(int)
例 2:
union U
{
short c[7];
int i;
}u;
int main()
{
printf("%d\n", sizeof(u));
return 0;
}
答案:16
最大成员大小为 5(short [7]),最大对齐数为 4(int)
四、枚举
枚举的作用类似 define,但是枚举将定义的常量有类型且以类型分组。
例:
enum Colour//颜色
{
BLUE,
GREEN,
RED
};
enum Sex//性别
{
MAN,
WOMAN
};
枚举内成员值
枚举内成员的值默认从 0 开始递增,可自定义。
例 1:
enum Colour
{
BLUE,
GREEN,
RED
};
int main()
{
printf("%d", BLUE);
printf("%d", GREEN);
printf("%d", RED);
return 0;
}
结果:0、1、2
例 2:
enum Colour
{
BLUE=1,
GREEN,
RED=5
};
int main()
{
printf("%d", BLUE);
printf("%d", GREEN);
printf("%d", RED);
return 0;
}
结果:1、2、5
枚举的使用
例:
enum Colour
{
BLUE=,
GREEN,
RED=
};
int main()
{
enum Colour c;
c = GREEN;
return 0;
}
错误用法:
enum Colour
{
BLUE,
GREEN,
RED
};
int main()
{
enum Colour c;
c = 1;
return 0;
}
因为 1 是整型,而 c 是 enum Colour 类型,不同类型不能进行赋值。
枚举相对于 define 的优点
(1)可一次性定义多个常量
(2)有类型,更严谨
(3)便于调试,程序执行前的预编译会进行宏替换,从而不能观察到 define 定义的值
(4)增加代码可读性