700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 【自定义类型】结构体 联合 枚举

【自定义类型】结构体 联合 枚举

时间:2022-08-18 00:16:30

相关推荐

【自定义类型】结构体 联合 枚举

在我们学习C语言的时候,有几个自定义类型需要我们掌握,那么我也是根据自己的学习来分享一下自己的理解。希望与大家互相学习,共同进步!

文章目录

一:结构体1.1:结构体的基础知识1.2:结构的声明1.3:特殊的声明1.4:结构体的自引用1.5:结构体变量的定义和初始化1.6: 结构体内存对齐1.7:修改默认对齐数1.8:结构体传参二:位段2.1:什么是位段2.2:位段的内存分配2.3:位段的跨平台问题三:枚举3.1:枚举类型的定义3.2:枚举的优点3.3:枚举的使用四:联合(共用体)4.1联合类型的定义4.2 联合的特点4.3:联合大小的计算

一:结构体

1.1:结构体的基础知识

结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。

1.2:结构的声明

struct tag{member-list;}variable-list;//全局变量

例如描述一个学生:

struct Stu{char name[20];//mingziint age;//nianlinchar sex[2];//xingbiechar id[20];//xuehao

1.3:特殊的声明

在声明结构体的时候。可以不完全的声明。

比如匿名结构体类型。

struct{int a;char b;float c;}x;struct{int a;char b;float c;}a[20], *p;

上面的两个结构体在声明的时候都省略掉了结构体标签(tag)。

那么问题来了?

p = &x; 合法吗?

警告:编译器会把上面两个声明当成完全不同的两个类型,所以是非法的。

1.4:结构体的自引用

在结构体中包含一个类型为该结构体本身的成员是否可以呢?

struct Node{int data;struct Node next;};

这种自引用是不可行的!

下面是正确的自引用方式:

struct Node{int data;struct Node * next;//结构体的指针类型};

1.5:结构体变量的定义和初始化

struct Point{int x;int y;}p1;//声明类型的同时定义全局变量p1;struct Point p2;//定义结构体变量p2;//初始化:定义变量的同时赋初值、struct Point p3 = {x, y};

1.6: 结构体内存对齐

我们已经掌握了结构体的基本使用,那么我们现在来讨论结构体的大小

int main(){struct s1{char c1;int i;char c2;};printf("%d\n", sizeof(struct s1));//12struct s2{char c1;char c2;int i;};printf("%d\n", sizeof(struct s2));//8struct s3{double d;char c;int i;};printf("%d\n", sizeof(struct s3));//16struct s4{char c1;struct s3 S3;int i;};printf("%d\n", sizeof(struct s4));//32system("pause");return 0;}

如何计算结构体大小?

首先得掌握结构体的对齐规则:

1:第一个成员在与结构体变量偏移量为0的地址处。

2:其他成员变量要对齐到对齐数的整数倍的地址处。

对齐数 = 编译器默认的一个对齐数与该成员大小的较小值。(VS中默认的值为8)

3:结构体的总大小为最大对齐数的整数倍。

4:如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(包括嵌套结构体的对齐数)的整数倍

为什么要存在内存对齐?

总的来说是:结构体的内存对齐是拿空间换取时间的做法!

那么在设计结构体的时候,我们既要满足对齐,又要节省空间**,那么就让占用空间小的成员尽量集中在一起。**

1.7:修改默认对齐数

之前我们见过了#pragma这个预处理指令,我们再次使用,可以改变我们的默认对齐数。

#pragma pack(8)struct s1{char c1;int i;char c2;};#pragma pack()//取消设置的默认对齐数,还原为默认!#pragma pack(1)//设置默认对齐数为1struct s2{char c1;int i;char c2;};#pragma pack()//取消设置的默认对齐数,还原为默认!int main(){//输出的结果是什么printf("%d\n", sizeof(struct s1));//12printf("%d\n", sizeof(struct s2));//6system("pause");return 0;}

结论:结构在对齐方式不合适的时候,我们可以自己更改默认对齐数。

1.8:结构体传参

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);//传结构体地址system("pause");return 0;}

上面的print1 和print 2 函数哪个好些?

答案是:首选print2 函数

原因:函数传参的时候,参数是需要压栈的,会有时间和空间上的系统开销。如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销较大,导致性能的下降。

结论:结构体传参的时候,要传结构体的地址。

二:位段

2.1:什么是位段

位段的声明和结构是类似的,有两个不同:

位段的成员必须是int,unsigned int , 或 signed int, short。位段的成员名后边有一个冒号和一个数字

比如:

struct A{int a : 2;int b : 5;int c : 10;int d : 30;};int main(){printf("%d\n", sizeof(struct A));//8system("pause");return 0;}

A就是一个位段类型,那位段的大小是多少?

理解:位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟,成员后面的数字代表所需要的bit位大小

2.2:位段的内存分配

位段的成员可以是int,unsigned int,signed int,或者 char(属于整型家族)类型。位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟。位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。

//一个例子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;

空间是如何开辟的?

2.3:位段的跨平台问题

int 位段被当成有符号数还是无符号数是不确定的。位段中最大位的数目是不能确定的。(16位机器最大16,32位机器最大32)。位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。

总结:跟结构相比,位段可以达到相同的效果,但是可以很好的节省空间,但是又有跨平台的问题存在。

三:枚举

枚举顾名思义就是举例,把可能的取值列举。

3.1:枚举类型的定义

enum Day{Mon,Tues,Wed,Thur,Fri,Sat,Sun};enum Sex{MALE,FEMALE,SECRET};

以上定义的enum Day,enum Sex,都是枚举类型。{}中的内容是枚举类型的可能取值,也叫枚举常量。这些默认取值是有值的。默认从0开始,依次递增1,当然在定义的时候也可以赋初值。

例如:

enum Color{red = 1,Green = 2,Blue = 4};

3.2:枚举的优点

我们可以使用#define定义常量,为什么非要使用枚举?

枚举的优点

增加代码的可读性和可维护性。和#define定义的标识符比较,枚举有类型检查,更加严谨。防止了命名污染(封装)。便于调试。使用方便,一次可以定义多个常量。

3.3:枚举的使用

enum Color{Red = 1,Green = 2,Blue = 4};int main(){enum Color clr = Green;system("pause");return 0;}

注意:只能使用枚举常量给枚举变量赋值,才不会出现类型的差异。

四:联合(共用体)

4.1联合类型的定义

联合也是一种特殊的自定义类型,这种类型定义的变量也包含一系列成员,特征是这些成员公用同一块空间

union Un{char c;int i;};int main(){union Un un;//联合变量的定义printf("%d\n", sizeof(un));//计算联合变量的大小system("pause");return 0;}

4.2 联合的特点

联合的成员是共用同一块内存的空间,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。

union Un{char c;int i;};int main(){union Un un;printf("%d\n", &(un.i));printf("%d\n", &(un.c));system("pause");return 0;}

4.3:联合大小的计算

联合的大小至少是最大成员的大小。当最大成员大小不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍。

union Un1{char c[5];int i;};union Un2{char c[7];int i;};int main(){printf("%d\n", sizeof(union Un1));//大小是8printf("%d\n", sizeof(union Un2));//大小是16system("pause");return 0;}

我们以上对结构体,位段,联合,枚举这几种自定义类型做出了基础层面上的解读,如果各位看官有收获的话还希望给个三连支持一波,有什么意见或者建议也请评论区指出,谢谢大家!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。