C语言自定义类型的保姆级讲解
前言
在我们日常写代码时,经常会遇到结构体类型的使用,今天带读者了解结构体类型的使用。
一、初始结构体
在了解结构体之前,我们先来了解一下结构体的基础只是,结构体到底是什么?
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。
下面举一个例子:
struct tag { menber_list; //成员列表 }variable_list; //变量列表
例如我们使用结构体描述一台电脑
struct computer { int price;//价格 char name[20];//名称 char brand[10];//品牌 }computer; //需要注意的是最后一行的“ ;”不能丢哦,不然编译器会报错提示你。
结构成员的类型
结构成员可以是标量数组、指针、甚至可以是其他的结构体。
示例:pandas 是基于numpy 的一种工具,该工具是为了解决数据分析任务而创建的。
匿名结构体类型
//匿名结构体类型 struct { int a; char b; float c; }x; struct { int a; char b; float c; }a[20], *p;
上面的代码中结构体省略掉了结构体标签,我们在添加一行代码
p=&x;
当我们编译时,会发现这样两个错误。
所以这样是不可行的。
结构体的自引用
就像函数递归一样,结构体也可以自己引用自己。他们的格式是这样的。
struct node { int data; struct node* next; };
结构体变量的定义和初始化
struct stu { char name[20]; int age; }; struct stu s = { "geralt ",100};
利维亚的杰洛特,100岁!
结构体内存对齐
各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数 各成员变量在存放的时候根据在结构中出现的顺序依次申请空间 同时按照上面的对齐方式调整位置 空缺的字节自动填充 同时为了确保结构的大小为结构的字节边界数(即该结构中占用最大的空间的类型的字节数)的倍数,所以在为最后一个成员变量申请空间后 还会根据需要自动填充空缺的字节。
简单的说就是:结构体的内存对齐是拿空间来换取时间的结果,提高了效率,浪费少许空间。
规则如下:
- 第一个成员在与结构体变量偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 - 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
vs编译器中默认对齐数是8。
当然了,作为创建vs,linux的我们是可以在c语言中自定义对齐数的。
我们可以使用#pragma这个预处理指令修改默认对齐数。
eg:#pragma pack(4) //修改对齐数为4
结构体传参
struct s { int data[1000]; int num; }; struct s s = { {1,2,3,4},100 }; //结构体传参 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;
和函数传参一样,形参是实参的一份临时拷贝,参数是需要压栈,会有时间和空间上的系统开销,如果传递的过程中结构体过大,就可能会导致系统开销大,导致性能的下降。我们不如直接传过去一份地址,再对它进行解引用操作符。
二、位段
位段与结构体不同的地方在于声明的时候,1.位段的成员必须是 int、unsigned int 或signed int 。2.位段的成员名后边有一个冒号和一个数字。
举一个例子:
struct a { int _a:2; int _b:5; int _c:10; int _d:30; };
- 位段的成员可以是 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;
那么位段的空间是如何开辟的呢?一张图搞定!
总的来说,位段和结构体相似,可以更好的节省空间,但是位段有跨平台的问题存在。
三、枚举
枚举枚举,顾名思义就是把可能出现的取值一一列举出来。
比如我们要描述一年的月份:
可以一一列举,我们要描述计算机语言的类型,也可以一一列举。
下面举一个例子
代码如下(示例):
enum computerlangue { c, java, python, };
每种结构体都有一定的优点,那么枚举有什么优点呢?
- 增加代码的可读性和可维护性
- 和#define定义的标识符比较枚举有类型检查,更加严谨。
- 防止了命名污染(封装)
- 便于调试
- 使用方便,一次可以定义多个常量
联合(公用体)
联合的特点是成员们公用同一块空间,因此他们也可以叫共用体
代码如下(示例):
//联合类型的声明
union un { char c; int i; };
//联合变量的定义 union un un; //计算连个变量的大小 printf("%d\n", sizeof(un));
三、 练习
在vc2013这款编译器中,这个结构体所占的空间大小是多少字节?
typedef struct{ int a; char b; short c; short d; }aa_t;
答案是:12个字节
为什么呢?结构体判断大小,一般向成员中最长的元素对齐。
在这4个结构成员中,最长的元素为a占四个字节。所以其他元素要向四对齐。a单独占四个字节,b为char类型占一个字节,c为short和b一起占四个字节并且空出一个字节。剩下d占一个字节,空两个字节。所以总占空间大小是4+1+2+2+3=12字节。
struct a { int a; short b; int c; char d; }; struct b { int a; short b; char c; int d; };
我们在来看一道题 :在32位系统环境,编译选项为4字节对齐,那么sizeof(a)和sizeof(b)是( )
答案是:16、12
最长的结构成员为int 占4个字节,b占两个字节,空下两个字节,c占4个字节,d占一个字节,空3个字节。
最长的类型为int,a占4字节,b和c一起占三个字节,还空下一个字节,d占4字节。
所以sizeof(a)sizeof(b)分别是16 12.
总结
我们介绍了c语言中的自定义类型,结构体,联合,枚举,位段,这些自定义类型可以帮我们更高效的使用c语言。