自定义类型详解(结构体+枚举+联合)C进阶
Posted 林慢慢i
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义类型详解(结构体+枚举+联合)C进阶相关的知识,希望对你有一定的参考价值。
前言:本章主要内容是C语言自定义类型中的结构体、枚举和联合。
结构体
结构
定义:结构是一些值的集合,这些值成为成员变量。结构的每个成员可以是不同类型的变量。
结构的声明
struct tag
{
member - list;
}variable-list;
例如描述一个学生:
struct stu
{
char name[30];//名字
int age;//年龄
char sex[5];//性别
};
特殊的声明(声明结构也可以不完全声明)
例如(省略结构体标签tag):
struct
{
int a;
char b;
float c;
}a;
struct
{
int a;
char b;
float c;
}a[30],*p;
注意:编译器会把上面两个声明当成完全不同的两个类型,所以使用 p=&x 非法。
结构的自引用
例如:
struct Node
{
int data;
struct Node* next;
};
结构体变量的定义和初始化
1.声明类型的同时定义变量p1
struct Point
{
int x;
int y;
}p1;
2.定义结构体变量p2
struct Point p2;
3.初始化:定义变量的同时赋初值。
3.1
struct Point p3 = { x , y };
3.2
struct Stu
{
char name[20];
int age;
};
struct Stu s = { "zhangsan",20 };
4.结构体嵌套初始化
4.1
struct Node
{
int data;
struct Point p;
struct Node* nest;
}n1 = { 10,{4,5},NULL };
4.2
struct Node n2 = { 20,{5,6},NULL };
结构体内存对齐
掌握了结构体的基本使用后,接下来介绍下如何计算结构体的大小,这就涉及到了一个热门考点:结构体内存对齐。
如何计算?首先得掌握结构体的对齐规则:
1.第一个成员在与结构体变量偏移量为0的地址处。
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数=编译器默认的一个对齐数与该成员大小的较小值。
VS中的默认值是8
3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
例题,计算下列结构体的大小。
struct S
{
char c1;
int i;
char c2;
};
struct S2
{
char c1;
int i;
double d;//8
};
struct S3
{
char c1;
char c2;
int i;
};
struct S4
{
double d;
char c;
int i;
};
struct S5
{
char c1;
struct S4 s4;
double d;
};
#include <stdio.h>
int main()
{
struct S s = {0};
struct S2 s2 = { 0 };
struct S3 s3 = { 0 };
struct S4 s4 = { 0 };
struct S5 s5 = { 0 };
printf("%d\\n", sizeof(s5));//32
printf("%d\\n", sizeof(s));//12
printf("%d\\n", sizeof(s2));//16
printf("%d\\n", sizeof(s3));//8
printf("%d\\n", sizeof(s4));//16
return 0;
}
注释:
以S为例,画下S的图示。其余类似,大家可以自己动手画图分析一波!
S的c1占用一个字节,浪费三个字节,int从下标4(第五个)开始,占用四个字节,c2占用一个字节,浪费3个字节,累计1+3+4+1+3=12;
S2的c1占用一个字节,浪费三个字节,int从下标4(第五个)开始,占用四个字节,d占用8个字节,累计1+3+4+8=16;
S3的c1占用一个字节,c2占用一个字节,然后浪费两个字节,int从下标4(第五个)开始,占用四个字节,累计1+1+2+4=8;
S4的d占用8个字节,c占用一个字节,然后浪费三个字节,int从下标12(第13个)开始,占用四个字节,累计8+1+3+4=16;
S5的c1占用1个字节,然后浪费7个字节,struct s4从下标8(第九个)开始(此时默认对齐数是8),占用16个字节,double d从下标24(第25个)开始,占用8个字节,累计1+7+16+8=32;
为什么存在内存对齐?
总结:结构体的内存对齐是拿空间换取时间
那在设计结构体的时候,我们既要满足内存对齐,又想节省空间,如何做到?
答:让占用空间小的成员尽量集中在一起
例题:
struct s1
{
char c1;
int i;
char c2;
};
struct s2
{
char c1;
char c2;
int i;
};
注释:上例中s1和s2类型的成员一模一样,但是s1占了12字节,s2占8字节,s2更具备优势。
修改默认对齐数
1.设置默认对齐数
#pragma pack(n)//默认对齐数为n
2.取消设置默认对齐数,还原为默认
#pragma pack()//取消默认对齐数
例子
#pragma pack(2)//默认对齐数为2
struct S
{
char c1;//0
int i;//
char c2;//
};
#pragma pack()//取消默认对齐数
#pragma pack(1)//默认对齐数为1
struct S
{
char c1;//1 1 1
int i;//4 1 1
char c2;//1 1 1
};
#pragma pack()//取消默认对齐数
int main()
{
printf("%d\\n", sizeof(struct S));
return 0;
}
注释:结构在对齐方式不合适的时候,我们可以自己更改默认对齐数。
结构体传参
例题(结构体传参和结构体地址传参)
struct S
{
int data[1000];
int num;
};
struct S s = { {1,2,3,4},1000 };
void print1(struct S s)//结构体传参
{
printf("%d\\n", s.num);
}
void print2(struct S* ps)//结构体地址传参
{
printf("%d\\n", ps->num);
}
int main()
{
print1(s);
print2(&s);
return 0;
}
注释:对比上面的print1和print2函数,print1的缺点很明显:在函数传参过程中,参数需要压栈,会有时间和空间上的系统开销。如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,会导致性能下降。
总的来说,结构体传参的时候,传结构体的地址(指针大小固定4/8)。
位段
什么是位段?
位段的声明和结构是类似的,注意两点不同:
1.位段的成员必须是int、unsigned int或signed int。
2.位段的成员名后边有一个冒号和一个数字。
比如:
struct A
{
//4个字节 - 32bit
int _a : 2;//_a 成员占2个bit位
int _b : 5;//_b 成员占5个bit位
int _c : 10;//_c 成员占10个bit位
//剩余15个bit
//4个字节 - 32bit
int _d : 30;//_b 成员占30个bit位
};
int main()
{
printf("%d\\n", sizeof(struct A));//8
return 0;
}
注释:A就是一个位段类型,大小是8字节。
位段的内存分配原则
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;
};
int main()
{
struct S s = { 0 };
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
return 0;
}
思考下上例中空间是如何开辟的?
答:
位段的跨平台问题
1.int位段被当成有符号数还是无符号数是不确定的。
2.位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出现问题)。
3.位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4.当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余位段还是继续利用剩余位段,这点不确定。
总结:
跟结构相比,位段可以达到同样的效果,还可以很好的节省空间,但是存在跨平台的问题。
位段的应用
在计算机网络的信息传输过程中,减小数据包的大小。
枚举
顾名思义就是把可能的取值一一列举。
比如:
月份有12个月,可以一一列举。
一周有七天,可以一一列举。
性别有男、女,可以一一列举。
颜色也可以一一列举。
这些例子里就可以使用枚举。
枚举类型的定义
星期、性别、颜色枚举类型举例
enum Day
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
enum Sex
{
MALE,
FEMALE,
SECRET
};
enum Color
{
RED,
GREEN,
BLUE
};
注释:以上定义的enmu Day,enum Sex,enum Color都是枚举类型。{}中的内容是枚举类型的可能取值,也叫枚举常量。
枚举常量都是有值得,默认从0开始,一次递增1,当然也可以在定义的时候赋初值。例如:
enum Color
{
RED=1,
GREEN=2,
BLUE=4
};
枚举的优点(明明已经有#define定义常量,为什么非要使用枚举?)
1.增加代码的可读性和可维护性
2.和#define定义的标识符相比枚举有类型检查,更严谨
3.防止命名污染(封装)
4.便于调试
5.使用方便,一次可以定义多个变量
枚举的使用
例子1:这样给clr赋值5可以吗?
enum Color
{
RED=1,
GREEN=2,
BLUE=4
};
enum Color clr = GREEN;
clr = 5;
答:不可以,枚举变量一经过定义赋值,不可再改变。
例子2:更加直观的显示所要实现算法的功能,而非简单的1、2、3、4.
void menu()
{
printf("*****************************\\n");
printf("**** 1. add 2. sub *****\\n");
printf("**** 3. mul 4. div *****\\n");
printf("**** 0. exit *****\\n");
printf("*****************************\\n");
}
enum Option
{
EXIT,//0
ADD,//1
SUB,//2
MUL,//3
DIV//4
};
int main()
{
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case ADD:
break;
case SUB:
break;
case MUL:
break;
case DIV:
break;
case EXIT:
break;
default:
break;
}
} while (input);
return 0;
}
联合(共用体)
联合也是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,特征是这些成员共用同一块空间(所以叫做共用体).
联合类型的声明、定义和联合类型变量大小计算:
union Un
{
short s[7];
int n;
};
Union Un un;
int main()
{
printf("%d\\n", sizeof(union Un));
return 0;
}
注释:结构体向int对齐,7个short一共是14字节,对齐后是16字节。n是单独的4字节,由于是union,所以n与s共用空间,只取最长的元素,故占用16字节。
联合的特点
联合的成员是共用同一块内存空间,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)
判断大小端存储
例题:若位顺序类似小端,低地址在低处,所以39是低地址,在低位,38在高位,结果就是3839;若类似大端存储,则结果是3938
int main()
{
union
{
short k;
char i[2];
}*s, a;
s = &a;
s->i[0] = 0x39;
s->i[1] = 0x38;
printf("%x\\n", a.k);
return 0;
}
注释:由输出结果可知位顺序类似小端,低地址在低处,所以39是低地址,在低位,38在高位,所以是3839。
联合大小的计算
1.联合的大小至少是最大成员的大小。
2.当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
例题:
int main()
{
union Un1
{
char c[5];
int i;
};
union Un2
{
short c[7];
int i;
};
printf("%d\\n", sizeof(union Un1));
printf("%d\\n", sizeof(union Un2));
return 0;
}
注释:Un1中,char c[5]至少占用6个字节,最大对齐数4,取最大对齐数的整数倍,结果为8;Un2中,short c[7]至少占用16个字节,最大对齐数4,取最大对齐数的整数倍,恰好结果为16.
自定义类型(结构体+枚举+联合)部分到此介绍结束了,感谢您的阅读!!!如果内容对你有帮助的话,记得给我三连(点赞、收藏、关注)——做个手有余香的人。
以上是关于自定义类型详解(结构体+枚举+联合)C进阶的主要内容,如果未能解决你的问题,请参考以下文章