C语言自定义类型重难点总结(结构体位段枚举联合)
Posted 燕麦冲冲冲
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言自定义类型重难点总结(结构体位段枚举联合)相关的知识,希望对你有一定的参考价值。
目录
一、结构体
1.1 结构体的特殊声明(匿名结构体类型)
struct
{
int a;
char b;
}x;
struct
{
int a;
char b;
}y;
上面的例子中省略了结构体标签
x = y;//将y中的成员赋值给x中的成员
尽管两个匿名结构体有相同的成员,但是编译器仍然将它们视为不同的两个构造体类型,因为它们是匿名的。
匿名结构体类型只可以用一次,并且无法用其再次创建对象,因为没有标签。
1.2 结构体的自引用
问题引入:一个结构体中是否能包含一个由自身创建的对象?
struct Person
{
int age;
struct Person relative;//创建一个亲戚对象
};
上述例子显然不对,可以巧妙地反证。
sizeof(struct Person)
假如成立,那么上述式子是无法计算出这个结构体的大小。
但是我偏要结构体中包含一个由自身所创建的对象,即结构体的自引用,那么该如何实现呢?
struct Person
{
int age;
struct Person* relative;
};
区别就在于,这个对象变成了结构体指针。
如若还不能理解,可以参照链表的相关知识进行深入理解。
如果再升一点难度,将结构体自引用与匿名结构体联系起来
typedef struct
{
int data;
Node* next;
}Node;
上述代码的意思是:首先创建一个匿名结构体,这个结构体包含一些成员,再为这个匿名结构体重定义,命名为Node。
但是这样写是错误的,因为对于一个结构体是先有成员后再有重定义的名字Node,所以其中的一个成员Node* next是非法的,Node还没有被重定义就被使用了。
解决方案
typedef struct Node
{
int data;
struct Node* next;
}Node;
1.3 结构体的内存对齐
问题引入:对于一个结构体,我们如何计算它所占空间的大小。
这个当中就涉及了近几年特别热门的知识点:结构体的内存对齐。
struct s1
{
char c1;
int i;
char c2;
};
问:求这个结构体所占空间的大小?
利用画图软件进行内存分析:
问:为什么存在内存对齐呢?
答:内存对齐可以实现牺牲空间来换取时间利用效率。
1.4 修改默认对齐数
#pragma pack(8)
上述代码意思是设置默认对齐数为8,一般放置于结构体之前。
#pragma pack()
上述代码表示:取消设置的默认对齐数,还原为默认。
一般设置于结构体之后,与设置默认对齐数的代码共同使用。
1.5 结构体传参
传参分为传值和传址两种,传址更适合于结构体传参,因为可以避免因为结构体过大,参数压栈开销大(临时拷贝),所导致的性能降低。
二、位段
2.1 枯燥但不完全枯燥的基本概念
问:什么是位段?
- 位段的成员必须是整形家族。
- 位段的成员名后有冒号和数字,表示该成员所占比特位大小。
- 位段的形式和结构体十分相似,唯一的区别就是成员后面有冒号和数字。
struct A
{
int a:2;
int b:5;
int c:10;
int d:30;
}
需要注意的是:冒号后的数字16位机器最大16,32位机器最大32
2.2 位段的内存分配
问:上述的结构体A该如何在内存中分配空间呢?
答:位段的空间是按照成员数据类型来开辟的,如果是int就以4个字节,如果是char就以1个字节的方式来开辟的。
那么上述代码,都为int类型,那么先开辟4个字节,a、b、c三者加起来占17个bit,那么还剩15个bit,不够存放d的30个bit,那么这时候就直接舍掉这15个剩下的bit,再开辟4个字节用于存放d,所以这个位段的大小就是8个字节。
2.3 位段的优势与劣势
优势:可以节省空间。
比如用二进制表示性别时,只有三种可能:男、女、保密,那么就只用开辟两个bit位。
劣势:跨平台时可能出现不兼容的问题。
比如万一有的编译器偏要把开辟空间中剩余的比特位也利用起来,而不是舍去。
三、枚举
3.1 枚举的优点
enum Color
{
RED,
GREEN,
BLUE
}
上述代码中RED默认值位0,如果不为其它枚举常量赋予初值的话,就依次往下递增1
问:能用#define定义常量,为什么要使用枚举?
答:好处一:可增加代码的可读性和可维护性。
比如在switch分支中,case所对应的选项可以运用枚举常量,而不是利用单纯的数字或者字符,利用有意义的单词可以让代码通俗易懂,同时也便于调试,还能一次性定义多个常量。
好处二:相较于#denfine定义的常量,枚举常量增加了类型检查
enum Color c = BLUE;
c = 2;//ok?
例如上述情况,第一行代码代表创建一个枚举对象,并为其赋值上BLUE(注意:枚举和结构体不同,枚举常量仅代表其创建的对象的可能取值),BLUE的值在上面的枚举中是2,所以给c赋值上2正确吗?
答:不正确。这样在cpp编译器下会出现类型不匹配的错误,提示为Color类型转换为了int类型。
而#define定义的常量不会出现这样的情况,所以不太利于程序的安全。
好处三:便于调试
好处四:相较于#define可以定义多个常量
四、联合(共用体)
4.1 基本概念
联合的声明举例
union Un
{
char c;
int i;
};
联合变量的定义
union Un un;
联合变量的大小计算
sizeof(un);//4
问题引出:联合变量是如何利用内存空间的?
答:联合的成员是共用一块内存空间的,比如上述例子中,c和i共用四个字节。
一个联合变量的大小,至少最大成员的大小。
printf("%p\\n", &(un.i));
printf("%p\\n", &(un.c));
由于这两个成员共用一块内存空间,所以它们所在的地址是相同的,那么上述代码的结果是相同的。
un.i = 0x11223344;
un.c = 0x55;
printf("%x\\n", un.i);
上述代码又涉及到了内存的分布了,所以还是画个图来展示。
4.2 联合大小的计算
- 联合的大小至少是最大成员的大小。
- 当最大成员的大小不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍。
union Un1
{
char c[5];
int i;
};
printf("%d\\n", sizeof(union Un1));
上述代码的结果就应该是8,而不应该是5,因为最大对齐数是4,而5不是4的倍数。
以上是关于C语言自定义类型重难点总结(结构体位段枚举联合)的主要内容,如果未能解决你的问题,请参考以下文章