欢迎来CILMY23的博客喔,本篇为【C语言结构体】用户自定义类型--结构体,结构体传参,位段,联合体和枚举【图文详解】,感谢观看,支持的可以给个一键三连,点赞关注+收藏。
前言
上一篇(http://t.csdnimg.cn/ruaRw)我们讲到C语言中,用户可以自定义类型,结构体,
本期将讲解结构体的传参,和位段,联合体以及枚举类型。
浮点数的取值范围:float.h 整型的取值范围:limits.h
今日语句分享:一个人走慢点也并无害处,因为他的辉煌不在于行走,而在于亲身体验。
目录
一、结构体传参
二、位段
三、联合体
四、枚举
一、结构体传参
结构体传参也分传值调用和传址调用
首先我们来看传值调用:
struct S
{int b[100];int num;
};void print1(struct S t)
{printf("%d %d %d %d\n", t.b[0], t.b[1], t.b[2], t.num);
}int main()
{struct S s = { {1,2,3},100 };print1(s);
}
结果如下:
我们接着来看传址调用
void print2(struct S* pt)
{printf("%d %d %d %d\n", pt->b[0], pt->b[1], pt->b[2], pt->num);
}int main()
{struct S s = { {1,2,3},100 };print2(&s);
}
结果如下:
二、位段
结构体传参后,我们得讲结构体的另外一个能力---位段
2.1什么是位段
位段就是以下这样的代码,它是基于结构体实现的,它的出现是为了节省空间,位是二进制位的意思
struct A
{int _a : 2;int _b : 5;int _c : 10;int _d : 15;
};
位段的声明和结构体声明是类似的,有两个不同:
1. 位段的成员必须是int、unsigned int 或signed int ,在C99中位段成员的类型也可以选择其他类型。
2. 位段的成员名后边有⼀个冒号和⼀个数字。
2.2位段的大小
位段的出现既然是为了节省空间,那上面那段代码如何解读呢?
如下所示:
实际上在冒号后面的数字代表的是bit,有几个数字就有几个bit位置。
#include <stdio.h>struct A
{int _a : 2;int _b : 5;int _c : 10;int _d : 15;
};struct B
{int _a;int _b;int _c;int _d;
};int main()
{struct A a;struct B b;printf("%d\n", sizeof(a));printf("%d\n", sizeof(b));
}
结果如下:
解释:
2.3位段的内存分配
看以下这段代码:
struct S
{char a : 3; char b : 4; char c : 5; char d : 4;
};int main()
{struct S s = { 0 };printf("%d ",sizeof(s));
}
结果如下:
我们看到大小是3,但是实际上
1. 位段中内存的空间使用是从左到右还是从右到左使用是不确定的
2. 当前面使用,剩余的空间不足下一个成员存储,是否继续使用剩余的空间是不确定的。
那我们接着看下面这段代码:
#include <stdio.h>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;printf("%d ",sizeof(s));
}
我们假设在vs上内存分布是从右到左的
这里有三个字节
首先a分配三个bit位,a的数值是10,转换成二进制是1010,因为只分配3个bit,所以存入010,b的数值是12,转换成二进制是1100,因为分配4个bit,而之前使用剩余空间足够,我们继续存入,c的数值是3,转换成二进制是011,而分配5个bit,所以存入011,其余用0补齐,d的数值是4,转换成二进制是100,分配4个bit,由于之前分配给c的bit位置不够了,我们再拿一个字节来存d,将100存入,然后再用0补齐
那在vs上到底是如何呢?
结果是显而易见的,正如我们所想的这样
2.3位段的跨平台问题
1. int在位段中被当成有符号数还是无符号数是不确定的。
2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。)
3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第⼀个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。
2.4 总结
位段总结:
1. 位段的成员可以是int,unsigned int,signed int 或者是char 等类型。
2. 位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的。
3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
三、联合体
像结构体一样,联合体也是由一个或者多个成员构成,这些成员可以不同的类型。但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同一块内存空间。所以联合体也叫:共用体。
3.1联合体的类型声明
union是联合体的关键字,我们对其声明也跟结构体类似
union U
{char c;int i;
};
3.2联合体的大小和内存分布
联合体的大小:
union U
{char c;int i;
};int main()
{union U u = { 0 };printf("%d ", sizeof(union U));
}
结果如下:
那为什么是4呢?这还要从联合体的成员地址看起
我们通过上图可以看到每个成员的地址的都是一样的 ,这说明联合体当中每个成员都是用同一个空间,所以给联合体其中一个成员赋值,其他成员的值也跟着变化。
3.3联合体大小的计算
union Un1
{char c[5];int i;
};union Un2
{char c[7];int i;
};
int main()
{//下⾯输出的结果是什么?printf("%d\n", sizeof(union Un1));printf("%d\n", sizeof(union Un2));return 0;
}
结果如下:
联合体同样涉及内存对齐
所以un1对齐到8字节,同样un2也是对齐到8字节
总结:
联合的大小至少是最大成员的大小。
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
3.4联合体的练习
写一个程序判断大小端字节序(这个我们之前写过:http://t.csdnimg.cn/gIUL5)
int check_sys()
{union U{int i;char c;}u;u.i = 1;return u.c;
}
那我们可以利用这个联合体共用一个空间的特性来解决大小端,然后设计一个函数,最后返回c 的值即可。
四、枚举
4.1枚举的类型声明
枚举顾名思义就是一一列举。
把可能的取值一一列举。
就比如以下这段代码:
enum Day
{Mon,Tues,Wed,Thur,Fri,Sat,Sun
};
以上定义的enum Day是枚举类型。{}中的内容是枚举类型的可能取值,也叫枚举常量。这些可能取值都是有值的,默认从0开始,依次递增1,当然在声明枚举类型的时候也可以赋初值。
enum Day//星期
{Mon = 8,Tues = 10,Wed = 99,Thur = 54,Fri = 55,Sat = 22,Sun = 66
};
4.2枚举的优点
我们可以使用 #define 定义常量,为什么非要使用枚举?
枚举的优点:
1. 增加代码的可读性和可维护性
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
3. 便于调试,预处理阶段会删除#define 定义的符号
4. 使用方便,⼀次可以定义多个常量
5. 枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用
4.3枚举类型的使用
假设我们想描述一个人的性别, 我们就可以用枚举常量给枚举类型的变量赋值。那是否可以拿整数给枚举变量赋值呢?在C语言中是可以的,但是在C++是不行的,C++的类型检查比较严格。
enum Sex
{ MALE,FEMALE,SECREAT
};int main()
{enum Sex S = FEMALE;return 0;
}
感谢各位同伴的支持,本期位段篇就讲解到这啦,如果你觉得写的不错的话,可以给个一键三连,点赞关注+收藏,若有不足,欢迎各位在评论区讨论。