今天我们来学习一下结构体,枚举以及联合等知识。
一。首先从结构体开始,结构体与数组一样都是聚合类型。
1.结构体的声明:
struct tag { member-list; }variable-list;
tag(1)可以省略(2)见名思意(3)尽量不省略。
member-list:不能为空。
variable-list:可以省略。例如:描述一位学生
struct Stu { char name[20]; int age; char sex[5]; char id[20]; };特殊的声明:在声明时可以不完全的声明。比如:
struct { int a; char b; float c; }x; struct { int a; char b; float c; }a[20],*p; p=&x;
这两个结构在声明时都省略了结构体标签tag。但p=&a;这是非法的,因为编译器会把上面的两个声明当成两个完全不同的两个类型。所以是非法的。
二。结构体的成员:可以是标量,数组,指针,甚至是其他结构体。那我们该如何访问这些成员呢?
结构体成员的访问:结构变量的成员通过点操作符(.)访问的。
struct S s; strcpy(s.name,"zhangsan"); //使用.访问name成员。 s.age=20; //使用.访问age成员。结构体访问指向变量的成员时有时候我们得到的不是一个结构体变量,而是指向一个结构体的指针。这时就该如下访问:
struct S { char name[20]; int age; }s; void print(struct S* ps) { printf("name =%s age =%d\n",(*ps).name,(*ps).age); printf("name =%s age =%d\n",ps->name,ps->age); }三。结构的自引用。例子:
struct Node { int data; struct Node next;(1) struct Node* next;(2) };(1)不可行,(2)可行。
四。结构体变量的定义和初始化。同时,结构体不能被整体赋值,这点和数组一样。
struct Point { int x; int y; }p1; //声明类型的同时定义变量p1. struct Point p2; //定义结构体变量。 struct Point p3={x,y}; //初始化:定义变量时赋值。 struct Stu //类型说明 { char name[15]; //名字 int age; //年龄 }; struct Stu ={"zhangsan",20}; //初始化 struct Node { int data; struct Point p; struct Node* next; }n1={10,{4,5},NULL}; //结构体嵌套初始化 struct Node n2={20,{5,6},NULL}; //结构体嵌套初始化
五。结构体内存对齐。
1,为什么存在内存对齐呢?(1)平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。(2)性能原因:数据结构(尤其是栈)应该尽可能的在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要依次访问。
2.掌握结构体的对齐规则:
(1)第一个成员在与结构体变量偏移量为0的地址处。(2)其它成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数=编译器默认的一个对齐数与该成员大小的较小值。VS默认8,Linux默认4.(3)结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。(4)如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
总体来说:结构体的内存对齐就是拿空间换取时间的做法。例题:
练习一 struct S1 { char c1; //1+3 int i; //4 char c2; // 1 }; printf("%d\n",sizeof(struct S1)); //1+3+4+1=9%4!=0,所以答案是12.
练习二 struct S2 { char c1; //1 char c2; // 1+2 int i; //4 }; printf("%d\n",sizeof(struct S2)); //1+1+2+4=8%4==0,所以答案是8.
练习三 struct S3 { double d; //8 char c; //1+3 int i; //4 }; printf("%d\n",sizeof(struct S3)); //8+1+3+4=16%8==0,所以答案是16.
练习四 struct S4 { char c1; //1+7 struct S3 s3; //16 double d; //8 }; printf("%d\n",sizeof(struct S3)); //8+1+7+16=32%8==0,所以答案是32.
只要学会这四道题,就证明你差不多掌握了内存对齐了。
六。结构体传参:跟数组不一样,它传参时不会发生降维。
struct S { int data[1000]; int num; }; struct S s={{1,2,3,4},1000}; //结构体传参 void print1(struct S s) { printf("%d\n",s.sum); } //结构体传参 void print2(struct S* ps) { printf("%d\n",ps->num); } int main() { print1(s); //传结构体 print2(&s); //传地址 return 0; }
很明显print2函数是更好的,节省时间。原因:函数传参的时候,参数是需要压栈的。如果传递一个结构体对象时,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降。
七。位段:位段的声明和结构是类似的,只有两个不同(1)位段的成员必须是int . unsigned int 或signed int,就是说成员要一样。(2)位段的成员名后边有一个冒号和一个数字。
struct A { int_a:2; //数字表示他们所占的比特数。 int _b:5; int_c:10; int _d:30; };
位段的内存分配:(1)位段的成员可以是int unsigned int signed int 或者是char(属于整形家族)类型。(2)位段的空间上是按照需要以四个字节(int)或者一个字节(char)的方式来开辟的。(3)位段涉及很多不确定因素,位段是不跨台的,注重可移植的程序应该避免使用位段。
位段的跨平台问题:(1)int位段被当成有符号数还是无符号数是不确定的。(2)位段中最大位的数目不能确定(16位机器最大为16,32位机器最大为32,写成27,在16位机器会出现问题)(3)位段中的成员在内存中从左向右分配,还是从右向左分配未定义。(4)当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。总结:跟结构相比,尾端可以到达同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。
八。枚举:顾名思义,把可能的取值一一列举。
1.枚举类型的定义
enum Day //星期 { Mon, Tues, Wednes, Thur, Fri, Sat, Sun }; enum Sex //性别 { MALE, FEMALE, SECURT }; enum Color //颜色 { red, grenn, blue };
以上定义的都是枚举类型,{}中的内容是枚举常量。
2.枚举的优点:(1)增加代码的可读性和可维护性(2)和#define定义的标识符比较枚举有类型检查,更加严谨。(3)防止了命名污染。(4)便与调试。(5)使用方便,一次可定义多个常量。
3.枚举的使用。
enum Color //颜色 { RED=1, GREEN=2, BLUE=4 }; enum Color clr=GREEN; //只能拿枚举常量给枚举常量赋值,才不会出现类型的差异。
九。联合(共用体):联合也是一种特殊的自定义类型,这种类型定义的变量包含了一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。
1.联合类型的声明
union Un { char c; int i; }; union Un un; //联合变量的定义。 printf("%d\n",sizeof(un));//计算联合变量的大小。
2.联合的特点:联合的成员时共用同一块内存空间的,这样的一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)
3.联合的大小。
(1)联合的大小至少是最大成员的大小
(2)当最大成员大小不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍。举例:
#include<stdio.h> #include<Windows.h> union Un1 { char c[5]; int i; }; union Un2 { short c[7]; int i; }; int main() { printf("%x\n",sizeof(union Un1)); printf("%x\n",sizeof(union Un2)); system("pause"); return 0; }答案是第一个是8,第二个是16.解析:Un1中,c[5]占5个,但是5不是最大对齐数4的整数倍,所以5+3=8.所以联合的大小为8.
同理第二个中,short[7]占14个,大于14且还是4的整倍数的数就是16,所以答案是16.
以上就是我的总结,谢谢观看。