一、结构体
1.概念
结构体:是一种聚合类型,可以将不同类型的变量放在一起。
数组:也是一种聚合类型,但是将相同类型的变量放在一起。
2.结构体声明
struct tag
{
member-list;
}variable-list;
tag:是一个标签,名称. : (1) 见名知意 (2) 可以省略,但不建议省略。
member-list: (1) 可以放任意类型的变量,可以套结构体。 (2) 不可以为空(c语言)
variable-list: (1) 变量列表,可定义多个,用逗号隔开 (2)可以省略,建议省略。
每个结构体都是一种类型。和内部元素类型是否一样没关系。
3.结构体成员
可以是标量、数组、指针、和其他结构体
4.结构体成员的访问
(1)结构体的地址和首元素的地址在数值上是一样的
(2) 地址是递增的
(3)通过两个操作进行访问:(1). 操作 (2)有时候我们得到的不是一个结构体变量,而是一个结构体指针,struct print (struct *p) 此时通过:(*p).name 或 p->name
5.结构体自引用
引用的是自身类型的指针,此时明确类型。
typedef:结构体类型重定义。(先定义后使用)
6.结构体的不完整声明
7.结构体变量的定义和初始化
初始化:定义变量的同时赋初值,这点和数组是一样。
struct stu p1-->定义变量
struct stu p2={"abcd",12}; -->初始化
8.结构体的内存对齐
内存对齐的原因:
(1) 平台原因(移植原因):不是所有的引荐平台都可以访问任意地址的任意数据
(2) 性能原因:处理器需作出两次访问
对齐规则:
(1) 第一个成员在与结构体变量偏移量为0的地址处。所以第一个元素无需对齐,默认就为对齐。
(2)其他成员变量要对齐到某个数字(对齐数)的整数倍地址处。 偏移量要能整除自己的对齐数。 对齐数:编译器默认的对齐数与该成员对齐数的最小值。 vs=8,linux=4
(3)结构体大小为最大对齐数的整数倍。
(4)如果嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体大小就是所有的最大对齐数的整数倍
总体说:结构体的你内存对齐实际就是用空间来换时间
我们要尽量将占用空间小的成员集中的一起。
struct s1
{
char c1; // 1
int i; //4->1+3+4
char c2; //1->8+1
};
int main(){
printf("%d\n", sizeof(struct s1));
system("pause");
return 0;
} // 结果为12
9.结构体传参
不会发生降维,不要传结构体变量,要传就传结构体指针。
以免造成系统开销过大。
二、位段
位段:节省空间 ,用时间换空间 内存填充(第一个变量内存空间能放则放,放不下在开辟)
与结构体的两个不同:
1.位段的成员必须全为是int、unsigned int、signed int 或char
按照需要以1字节或者4字节开辟
2.位段成员后面必须有冒号
struct A
{
int _a : 2; 2个bit位
int _b : 5; 5个bit位
int _c : 10; ...
int _d : 30; ...
};
3.移植性差。
int被当做有符号无符号不确定。
最大位数目不确定
位段中是从左到右分配还是从右向左分配不确定。
当一个结构包含两个位段,第二个成员较大无法容纳第一个位段的剩余位,是舍弃剩余位还是利用剩余位不确定。
4.应用:网络协议、报头设计(可以理解为相当于快递盒子)。
总结:和结构体比较,位段能够达到相同的效果,而且可以很好的节省空间,但是跨平台问题存在。
三、枚举
概念:把可能取得值一一列举
enum day
{
Mon,
Tues,
Wed,
Thur,
};
这里定义的enum Day就为枚举类型
{}里面的可能取值叫枚举常量,这些值都是默认从0开始,一次递增1,当然在定义的时候也可以赋初值。
优点:
1.增加代码可读性和可维护性。
2.和define相比更加严谨,因为预处理会进行语法处理。
3.使用方便,可一次定义多大常量。
注意!
只能拿枚举常量给枚举变量赋值。且只可以赋此枚举类型里面的枚举常量。
四、联合(共用体)
特征:所有成员公用同一块空间,这样联合体的大小至少是最大成员的大小。
union un
{
char c;
int i;
};
我们可以利用联合体的特征来判断当前计算机的大小端存储
union node
{
char c;
int num;
};
int main(){
union node p;
p.num = 0x12345678;
if (p.c == 0x78){
printf("little");
}
else{
printf("big");
}
system("pause");
return 0;
}
在这里我提供一种判断计算机大小的方法:
int check_eye()
{
int num = 1;
char *p= (char *)&num // 只看第一个字节(最低地址)所以强转
if (*p == 1)
return 0;
else
return 1;
}
int main()
{
int ret = check_eye();
if (ret == 0)
printf("little");
else
printf("big");
system("pause");
return 0;
}
联合体大小的计算:
当最大成员大小不是最大对齐数的整数倍时候,就要对起到最大整数倍处。
union un
{
char c[5]; //1->5
int i; //4->4
};
int main(){
printf("%d\n", sizeof(union un));
system("pause");
return 0;
} //结果为8
使用:
可以将整形类型的IP地址转为点分十进制表示形式。
union ip_addr
{
unsigned long addr;
struct{
unsigned char c1;
unsigned char c2;
unsigned char c3;
unsigned char c4;
}ip;
};
int main(){
union ip_addr my_ip;
my_ip.addr = 176238749;
printf("%d.%d.%d.%d\n", my_ip.ip.c4, my_ip.ip.c3, my_ip.ip.c2, my_ip.ip.c1 );
system("pause");
return 0;
}