自定义类型
Posted L_add
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义类型相关的知识,希望对你有一定的参考价值。
自定义类型
结构体
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量
结构体声明
struct Book
{
//成员变量
char name[20];
char author[20];
short price;
};
//typedef 相当于定义了一个小名
typedef struct nub
{
//成员变量
char name[20];
char author[20];
short price;
}nub;
int main()
{
struct Book b1 = { "C语言程序设计", "谭浩强", 45 };
nub n1 = { "hah", "llll", 90 };
return 0;
}
匿名结构体,特点:只能用一次
struct
{
char c;
int a;
short s;
}S;
struct
{
char c;
int a;
short s;
}*ps;
结构体自引用
在结构中包含一个类型为该结构体本身的成员
struct Node
{
int data;
struct Node * n;
};
结构体变量的定义和初始化
struct S
{
int a;
int b;
double d;
};
struct B
{
char c;
struct S s;
short ss;
};
int main()
{
struct B b1= { 'a', { 100, 200, 3.14 }, 5 };
printf("%lf\\n", b1.s.d);
}
结构体内存对齐
结构体在计算大小时,会发生内存对齐
结构体对齐规则:
1、第一个成员在与结构体偏移量为0的地址处
2、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
对齐数=编译器默认的一个对齐数与该成员大小的较小值
vs中默认是8,Linux默认对齐数是4
//结构体在计算大小时,会发生内存对齐
//offsetof 计算的是结构体成员相对于结构体起始位置的偏移量
#include <stddef.h>
struct S1
{
char c1;
int i;
char c2;
};
int main()
{
struct S1 s1;
//printf("%d\\n", sizeof(s1));//12
printf("%d\\n", offsetof(struct S1, c1));//1
printf("%d\\n", offsetof(struct S1, i));//4
printf("%d\\n", offsetof(struct S1, c2));//8
return 0;
}
3、结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
4、如果嵌套结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大的对齐数(含嵌套结构体的对齐数)的整数倍
struct S3//16
{
double d;//8
char c;//1
int i;//4
};
struct S4
{
char c1;//1
struct S3 s3;//16
double d1;//8
};
int main()
{
struct S4 s4;
printf("%d\\n", sizeof(s4));//32
return 0;
}
为什么存在内存对齐?
1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据;某些硬件平台上只能在某些地址处取某些特定类型的数据,否则会出现抛出硬件异常
2、性能原因:数据结构(尤其是栈)应尽可能的在自然边界上对齐,原因在于,为了访问未对齐的内存,处理器需要做两次内存访问;而对齐的内存访问只需要一次。
总体来说:
结构体的内存对齐是拿空间换时间
如何在满足对齐时又节省空间?
1、让占用空间小的成员尽量集中在一起
struct S1
{
char c1;
char c2;
int i;
};
int main()
{
struct S1 s1;
printf("%d\\n", sizeof(s1));//8
return 0;
2、修改默认对齐数
#pragma pack(1)//设置默认对齐数
struct S1
{
char c1;
int i;
char c2;
};
#pragma pack()//恢复默认对齐数
int main()
{
printf("%d\\n", sizeof(struct S1));//6
return 0;
}
结构体传参
struct S
{
int data[20];
int num;
};
void Print(struct S s){}
void Print2(struct S* ps){}
int main()
{
Print(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 main()
{
struct A a1;
//一次开辟一个整形4个字节
//4 -》32bit
//_a - 2
//_b - 5
//_c - 10
//4 ->32bit
//_d - 30
printf("%d\\n", sizeof(struct A));//4+4 = 8
return 0;
}
位段的内存分配
1、位段成员可以是int,unsigned int,signed int ,char(属于整形家族)类型
2、位段的空间上是按需要以4个字节(int)或是1个字节(char)的方式来开辟的
3、位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段
位段的跨平台问题
1、int位段被当成有符号还是无符号是不确定的
2、位段中最大位数不能确定(16为机器最大16,32位是32,写成27,在16位机器会出现问题)
3、位段中成员在内存中从左向右分配,还是从右向左分配标准尚未定义
4、当一个结构体包含两个位段,第二个位段较大,无法容纳于第一个位段剩余的位时,是舍弃还是利用是不确定的
总结:跟结构体相比,位段可以达到同样的效果,可以节省空间,但是有跨平台的问题存在
枚举
枚举—列举,把可能的值一一列举
枚举类型的定义
取值默认从0开始,也可以赋值,一次递增1;
enum RGB
{
red,
green,
blue
};
int main()
{
printf("%d\\n", Tues);
printf("%d\\n", red);//0
printf("%d\\n", green);//1
printf("%d\\n", blue);//2
return 0;
}
enum Day
{
//枚举类型可能取值
Mon = 3,
Tues,
Wed,
Thus,
Fri,
Sat,
Sun
};
int main()
{
printf("%d\\n", Tues);//4
return 0;
}
枚举的优点
可以使用#define定义常量
优点:
1、增强代码可读性
2、和#define定义的标识符比较枚举有类型检查,更加严谨
3、防止命名污染(封装)
4、便于调试
5、使用方便,一次可以定义多个常量
联合
联合类型的定义
联合是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,特征是这些成员公用一块空间(所以联合体也叫共用体)
union Un//联合类型的声明
{
char c;
int i;
};
int main()
{
union Un u;//联合类型的定义
return 0;
}
联合的特点
联合的成员是共用一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小
union Un
{
char c;
int i;
};
int main()
{
union Un u;//联合类型的定义
printf("%d\\n", sizeof(u));//4
printf("%p\\n", &u);
//c和i会公用一块空间
printf("%p\\n", &u.c);
printf("%p\\n", &u.i);
return 0;
}
在同一时间内,联合体内的成员只能使用一个
union Un
{
char c;
int i;
};
int main()
{
union Un u;//联合类型的定义
u.i = 0x11223344;
u.c = 0x55;
printf("%x\\n", u.i);//0x1122334455
return 0;
}
其中一个成员改变,其他的成员也改变
判断计算机是大端还是小端
int check_sys()
{
union Un
{
char c;
int i;
}u;
u.i = 1;
return u.c;
}
int main()
{
int ret = check_sys();
if (ret == 1)
printf("小端\\n");
else
printf("大端\\n");
return 0;
}
联合大小的计算
- 联合的大小至少是最大成员的大小
- 当最大成员大小不是最大对齐数的整数倍的时候,就对齐到最大对齐数的整数倍
union U
{
char arr[6];//6
int i;//4
};
int main()
{
union U u;
printf("%d\\n", sizeof(u));//8
return 0;
}
union U
{
short arr[7];//14
int i;//4
};
int main()
{
union U u;
printf("%d\\n", sizeof(u));//16
return 0;
}
以上是关于自定义类型的主要内容,如果未能解决你的问题,请参考以下文章