构造体
构造体定义方法
定义方法一:
struct MyStruct //定义构造体,定义的构造体在内存中是不占空间的{ int a; char b; short c;}; //没有创建对象int main(void){ //应用构造体,创立一个构造体对象,创建对象时才会占用内存空间 struct MyStruct a = {10,'b',4}; //能够在创立时就初始化,也能够在之后赋值 struct MyStruct* sp = &a; //构造体指针 a.a = 10; (*sp).b = 'b'; sp->c = 4;}
定义方法二:
struct MyStruct{ int a; char b; short c;}s1,s2,s3; //s1,s2,s3 是创立的构造体对象,在定义构造体的同时创建对象//s1,s2,s3 是全局变量 int main(void){ s1.a = 10; //这里能够间接应用构造体对象}
定义方法三:
typedef struct MyStruct //相当于对这个构造体重命名为 Stu{ int a; char b; short c;}Stu; int main(void){ Stu s1; //应用重命名,进行创建对象。}
构造体作为参数时
- 函数传参时,参数是须要压栈的
- 如果传递一个构造体对象时,间接传递构造体实参,在函数那边会压栈一个同样大小的内存空间(创立构造体形参),会导致系统开销增大,从而导致性能升高。
- 所以传递构造体参数时,尽量传递构造体的指针(地址)
匿名构造体类型
- 留神:如果两个匿名构造体的成员一样,然而在编译器看来也是不同的构造体类型。
struct //没有申明构造体的名称{ int a; char b; short c; }x; //定义了一个构造体的对象//这种申明构造体办法,只能在申明时定义构造体对象,之后不能在定义对象(没有构造体名称)struct { int a; char b; short c; }p; //p和x是不同的构造体类型。
构造体的自援用
struct Node{ int date; struct Node* next; //用指针指向下一个同类构造体的地址。 //struct Node n; //在构造体中创立本人类型的对象 //这种用法是谬误的,会呈现套娃的状况,内存会无限大,零碎报错}
构造体内存对齐
- 先看示例:
struct X { int a; char b; short c; }; struct P { char a; int b; char c short d; struct X st;}; struct P a;printf("%d\n",sizeof(p)); //输入后果为 20
构造体对齐规定
- 第一个成员,寄存在构造体变量偏移量为0的地址处(即构造体变量的首地址处)
其余成员要对齐到某个数(对齐数)的整数倍的地址处。
- 对齐数 = 编译器默认的对齐数 和 该成员大小 的较小值
- VS 中默认对齐数为 8。
- 每个成员都有一个对齐数(可能会不雷同)
- 构造体的总大小为最大对齐数的整数倍。
- 如果有嵌套构造体,嵌套构造体对齐到字节的最大对齐数的整数倍处,构造体的整体大小就是最大对齐数(包含嵌套构造体成员的对齐数)的整数倍。
为什么存在内存对齐
- 移植起因:不是所有硬件平台都能任意地址拜访,某些硬件平台只能在某些地址处取某些特定类型的数据。
- 性能起因:数据结构(尤其是栈)应该尽可能的在边界上对齐。为了拜访未对齐的内存,处理器须要作两次内存拜访,而对齐内存的拜访只须要拜访一次。
- 总的来说就是:构造体的内存对齐是拿空间来换取工夫。
- 所以在设计构造体时,尽量将空间小的成员放在一起,节俭空间。
批改默认对齐数#pragma pack
- 将构造体放在#pragma pack之间,该构造体的默认对齐数就变为4。
#pragma pack(4)struct X { int a; char b; short c; }; #pragma pack()
宏offsetof()求偏移量
offsetof(type, member-designator);int i = offsetof(struct X,b); //返回值为b的偏移量
- 函数作用 :求构造体成员相当于首地址的偏移量
- type :构造体类型
- member-designator:构造体成员
- 返回值为size_t ,示意构造体成员的偏移量。
构造体与位段
- 位段的定义方法和构造体相似。
- 位段成员的前面有一个冒号和数字。(数字是这个变量的大小bit位)(不是字节哦)
- 位段成员只能是 unsigned int 和 signed int 或者char类型。
- 位段不存在内存对齐(节俭空间)
- 位段是不能跨平台的,所以可移植程序中不能应用位段。
//定义方法struct MyStruct{ int _a : 2; //_a这个变量只占2个bit位 int _b : 5; //_b这个变量只占5个bit位 unsigned int _c : 10; //_c这个变量只占10个bit位 unsigned int _d : 30;};//总共37个bit位,占两个int型8字节
枚举
- 把可能的值一一列举进去。
//枚举的定义方法enum Day //一周{ Mon, Tues, Wed, Thur, Fri, Sat, Sum};
- 如果定义枚举时,如果没有初始化,则默认从0开始顺次增大1。
- 枚举外面的都是常量,都是枚举类型,只能在定义时初始化,之后无奈赋值。
联合体(共用体)
- 共用体中的成员共用同一个内存空间(即所有成员的地址都一样)
- 留神:共用体是成员的地址雷同,并且那个空间存的数据也雷同
- 然而每个成员的值是不同的(因为变量类型不同,解析办法不同)
union Un //定义共用体{ char c; int i; short j;};union Un u;u.i = 123456;printf("%d\n", u.c); //64printf("%d\n", u.i); //123456printf("%d\n", u.j); //-7616
- 留神:共用体定义时不能初始化,只能在创立共用体对象后能力赋值。
联合体的对齐
- 共用体的总大小为最大对齐数的整数倍。
- 共用体的总大小至多是成员变量中空间最大的那个。
union Un //该共用体大小为 4 (int的对齐数为4){ char c; int i; short j;};union Un //大小位16,int为4字节,arr占14字节,所以总大小为16字节{ short arr[7]; int i;};