进空间的网站,什么是竞价推广,自己制作网站的步骤,设计上海2023展会时间目录
前言
结构体
1含义
2语法
3匿名结构体
4结构体自引用
5结构体的定义与初始化
6内存对齐
7修改对齐数
8结构体传参
位段
1含义
2位段的内存分配
编辑3位段的问题
4位段的应用
枚举
1含义
2定义
3枚举优点
4枚举使用
联合
1含义
2定义
3特点
4计…目录
前言
结构体
1含义
2语法
3匿名结构体
4结构体自引用
5结构体的定义与初始化
6内存对齐
7修改对齐数
8结构体传参
位段
1含义
2位段的内存分配
编辑3位段的问题
4位段的应用
枚举
1含义
2定义
3枚举优点
4枚举使用
联合
1含义
2定义
3特点
4计算
通讯录 前言
C语言的结构体知识学好了的话对后面学习C类对象就简单很多了
结构体
1含义
结构体是一些值的集合这些值称为成员变量每个成员变量可以是不同类型的变量。
2语法
struct tag//类型名
{member-list;//成员变量
}variable-list;//可在此时定义出结构体类型的变量 它是全局变量
假如我们用结构体来描述一个学生
struct Stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号
}S1;//分号不能丢
3匿名结构体
也就是结构体不完全声明
//匿名结构体类型
struct
{int a;char b;float c;
}x;
匿名结构体省略了tag(结构体类型名)而且它只能用一次(声明后立即定义不能声明后定义)
能否用指针来接收匿名结构体变量
不能编译器会认为这个两个类型vs2019编都编不过vs2022能编过 4结构体自引用
struct Node
{int data;struct Node next;
};int main()
{printf(%d, sizeof(struct Node));return 0;
}
上面这种引用行吗 编译器认为你在内部定义了同一个结构体成员那这个成员变量里又包含了同一个结构体成员...这个大小是无穷大的 所以正确的结构体自引用
struct Node
{int data;struct Node* next;
};
这种在链表中用的最多
其它的自引用定义
typedef struct Node
{int data;N* next;
}N;
这种写法是错误的结构体typedef之前内部并不知道
//正确写法
typedef struct Node
{int data;struct Node* next;
}N;
5结构体的定义与初始化
struct Poin
{int x;int y;
}p1 {1,2};//可以在这里初始化struct stu
{int date;struct Poin p;
}s1;//全局变量int main()
{struct stu s2 { 100,{20,30} };//局部变量 按顺序初始化//struct stu s2 { .p{20,30},.date100 };//指定成员初始化printf(%d %d %d\n, s2.date, s2.p.x, s2.p.y);return 0;
}
6内存对齐
计算结构体的大小这个非常重要的一个内容
来看下面的一段代码
struct s1
{char a;int b;char c;
};struct s2
{char a;char c;int b;
};int main()
{printf(s1的大小:%d\n, sizeof(struct s1));printf(s2的大小:%d, sizeof(struct s2));return 0;
} int是4字节char是1字节两个int和一个char加起来不是6字节吗 不同的结构体放着相同的变量只是顺序不同为什么大小不一样 介绍一个宏offsetof用来查看结构体变量相对于内存的偏移位置 按照打印出来的内存偏移位置进行安排成员变量的放置 一共9个字节就能结束了偏偏还要在浪费3个字节前面也浪费3个字节为什么 这时我们来看看内存对齐的规则 1. 第一个成员在与结构体变量偏移量为0的地址处。 2. 其他成员变量要对齐到对齐数整数倍的地址处。对齐数 编译器默认的一个对齐数 与 该成员大小的较小值在VS中默认的值为8 而gcc是没有默认对齐数的 3. 结构体总大小为最大对齐数每个成员变量计算出来的对齐数的最大值的整数倍。 4. 如果嵌套了结构体的情况嵌套的结构体对齐到自己的最大对齐数的整数倍处 结构体嵌套的结构体大小
#includestdio.hstruct S3//总大小16
{double d;//8 8 - 8char c; //1 8 - 1int i; //4 8 - 4
};
struct S4
{char c1; //1 8 - 1struct S3 s3;//16 8 - 8double d; //8 8 - 8
};
int main()
{printf(%d\n, sizeof(struct S4));return 0;
} 那为什么存在内存对齐? 1. 平台原因(移植原因) 可能硬件平台只能在某些地址处取某些特定类型的数据(否则硬件中断)2. 性能原因 为了访问未对齐的内存处理器需要作两次内存访问而对齐的内存访问仅需要一次访问 这本质上就是以空间换时间来提高效率 那在设计结构体时既要省空间又要提高效率
尽量让字节小的成员变量放在一起
#includestdio.hstruct s1
{char c1;int i;char c2;
};struct s2
{char c1;char c2;int i;
};int main()
{printf(%d %d, sizeof(struct s1), sizeof(struct s2));return 0;
} 7修改对齐数
如果对应VS的默认对齐数不满意最好不修改可以自己设定对齐数
#pragma pack(1)
//在#pragma的范围内的对齐数是1
struct s3
{char c1;int i;char c2;
};
#pragma()struct s4
{char c1;int i;char c2;
};int main()
{printf(s3%d s4%d, sizeof(struct s2), sizeof(struct s3));return 0;
} 8结构体传参
struct S
{int data[1000];int num;
};
struct S s { {1,2,3,4}, 1000 };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;
} 关于结构体传参尽量使用传址传参 因为一个结构体大小可能很大如果传值传参会加大系统资源的开销导致性能的下降而使用传址传参时数据不希望进行修改要把const加上 位段
1含义 位段是一种特殊的自定义类型 对空间的利用精打细算 a 位段的成员必须是 int、char(整形家族) b 位段的成员名后边有一个冒号和一个数字 struct A
{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};
与结构体相比大小方面有什么不同吗
struct A//如果是结构体
{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};struct B
{int _a;int _b;int _c;int _d;
};int main()
{printf(A%d B%d, sizeof(struct A),sizeof(struct B));return 0;
} 答案位段比结构体大小要小更节省空间了那这是怎么实现的呢
2位段的内存分配 位段后面的数字代表要分配多少个bit空间给该成员 位段的空间是按照需要以4个字节 int 或者1个字节 char 的方式来开辟的 #includestdio.hstruct 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;return 0;
}
3位段的问题 1. int 位段被当成有符号数还是无符号数 2. 位段中最大位的数目 16位机器最大1632位机器最大32如果写成27在16位机器会出问题 3. 位段中的成员在内存中从左向右分配还是从右向左分配 在VS时从右向左 4. 开辟的空间无法容纳下个位段时是舍弃后另外开辟空间还是利用 在VS时舍弃剩余的位 4位段的应用
应用于各种协议信息传到下一层时需要用到 枚举
1含义
枚举就是一一列举在现实生活中把一个星期进行枚举星期一星期二星期三... 那么枚举怎么用代码来表示呢
2定义
enum Day//星期
{Mon 1,Tues,Wed,Thur,Fri,Sat,Sun
}; enum Day是一个枚举类型 枚举括号内的内容都是常量 枚举默认从0开始与数组一样但你也可以自己修改(Mon 1) 3枚举优点
以上对于星期的枚举我们也可以用#define Mon 1#define Tues 2来表示那是不是就说枚举就可以被替换了 a 使用枚举方便调试而#define在调试前就被替换成常量 b 枚举有类型检查更严谨写 b 使用枚举能增加代码的可读性与可维护性 c 枚举内定义变量不存在命名冲突而#define可能有命名污染问题 4枚举使用
enum Color//颜色
{RED1,GREEN2,BLUE4
};
int main()
{enum Color clr GREEN;//只能拿枚举常量给枚举变量赋值才不会出现类型的差异return 0;
}
联合
1含义
联合也是一种特殊的自定义类型 这种类型定义的变量也包含一系列的成员这些成员公用同一块空间所以联合也叫共用体。
2定义
#includestdio.h
//联合类型的声明
union Un
{char c;int i;
};
int main()
{union Un un;printf(%d\n, sizeof(un));return 0;
} 3特点 a 由于联合类型共有内存空间所以比其它的结构体而言节省空间 b 成员共有内存空间所以定义变量时成员不能同时出现后一个会改变前一个的数据 使用场景1判断大小端之前通过地址方式访问判断
#includestdio.h
int sys_call()
{//匿名联合union{char c;int i;}Un;Un.i 1;// 01 00 00 00 还是 00 00 00 01//(c与i公用1字节的内存空间)return Un.c;
}int main()
{if (sys_call()){printf(小端\n);}else{printf(大端\n);}return 0;
}
使用场景2设计一个礼物清单
struct git_list
{double price;char name[20];char author[20];int page;char form[20];double size;char flavour[20];char materials[20];
};//利用联合体设计清晰且省空间
struct git_list
{double price;char name[20];union{char author[20];int page;}book;union{char form[20];double size;}mug;union{char flavour[20];char materials[20];}cooike;
};
4计算 a 联合的大小至少是最大成员的大小 b 当最大成员大小不是最大对齐数的整数倍的时候就要对齐到最大对齐数的整数倍 所以联合大小 ! 最大成员大小 #includestdio.hunion Un1
{char c[5];int i;
};int main()
{printf(%d\n, sizeof(union Un1));return 0;
}
你可能会因为大小是5(char c[5]一共是5字节比i 4字节大)但实际上 最大是5没错但它不是最大对齐数max(charint) 4的整数倍进行要进行对齐 通讯录 实现一个通讯录具备以下功能 1增加联系人(名字年龄性别电话地址) 2删除联系人的信息 3查找联系人的信息 4更改联系人的信息 5打印通讯录 6对通讯录进行排序 //Contact.h
#define _CRT_SECURE_NO_WARNINGS 1
#includestdio.h
#includestdlib.h
#includestring.h
#includeassert.h#define Max_Size 100
#define Max_NameSize 20
#define Max_SexSize 5
#define Max_CallSize 20
#define Max_AddrSize 30enum
{ext,add,del,check,modify,print,sort,
};typedef struct Mess
{char name[Max_NameSize];int age;char sex[Max_SexSize];char call[Max_CallSize];char addr[Max_AddrSize];
}Mess;typedef struct Contact
{int sz;Mess message[Max_Size];
}Contact;void Add(Contact* con);
void Del(Contact* con);
void Check(const Contact* con);
void Modify(Contact* con);
void Print(const Contact* con);
void Sort(Contact* con);//Contact.c
#includecontact.hvoid Add(Contact* con)
{assert(con);if (con-sz Max_Size){printf(Contact Is Full\n);return;}printf(Input Name:);gets(con-message[con-sz].name);//scanf(%s, con-message[con-sz].name);printf(Input Age:);scanf(%d, con-message[con-sz].age);//如果输入的是非法字符串?getchar();//清空缓冲区的\nprintf(Input Sex:);gets(con-message[con-sz].sex);//scanf(%s, con-message[con-sz].sex);printf(Input Call:);gets(con-message[con-sz].call);//scanf(%s, con-message[con-sz].call);printf(Input Addr:);gets(con-message[con-sz].addr);//scanf(%s, con-message[con-sz].addr);con-sz;printf(Add Finish\n);
}void Print(const Contact* con)
{assert(con);if (con-sz 0){printf(Contact Is Empty\n);return;}printf(%-20s %-5s %-5s %-11s %-30s\n, name, age, sex, call, addr);for (int i 0; i con-sz; i){printf(%-20s %-5d %-5s %-11s %-30s\n,con-message[i].name, con-message[i].age, con-message[i].sex,con-message[i].call,con-message[i].addr);}
}static int Find(const Contact* con,char Name[])
{for (int i 0; i con-sz; i){if (strcmp(con-message[i].name,Name)0)//用strcmp比{printf(Find Sucess\n);return i;}}printf(Find Error\n);return -1;
}void Del(Contact* con)
{assert(con);char InputName[Max_NameSize];printf(Input Del Name:);gets(InputName);int DelPos Find(con, InputName);if (DelPos -1) return;for (int i DelPos; i con-sz - 1; i){con-message[i] con-message[i 1];//后一个往前一个覆盖}con-sz--;printf(Del Sucess\n);}void Check(const Contact* con)
{assert(con);printf(Input Check Name:);char Check_Name[Max_NameSize];gets(Check_Name);int pos Find(con, Check_Name);if (pos -1) return;printf(Check Result:\n);printf(%-20s %-5s %-5s %-11s %-30s\n, name, age, sex, call, addr);printf(%-20s %-5d %-5s %-11s %-30s\n,con-message[pos].name, con-message[pos].age,con-message[pos].sex, con-message[pos].call,con-message[pos].addr);
}void Modify(Contact* con)
{assert(con);char Modify_Name[Max_NameSize];printf(Input Modify Name:);gets(Modify_Name);int pos Find(con, Modify_Name);if (pos -1) return;printf(Modify Name:);gets(con-message[pos].name);printf(Input Modify Age:);scanf(%d, con-message[pos].age);getchar();printf(Input Modify Sex:);gets(con-message[pos].sex);printf(Input Modify call:);gets(con-message[pos].call);printf(Input Modify addr:);gets(con-message[pos].addr);printf(Modify Sucess\n);
}int cmp(const void* a, const void* b)
{return ((*((Contact*)a)).message-age - (*((Contact*)b)).message-age);
}void Sort(Contact* con)
{qsort(con, con-sz, sizeof(con-message[0]), cmp);printf(排序完成\n);
}//test.c
#includecontact.hvoid memu()
{printf(********************************\n);printf(***** 0.exit 1.add *****\n);printf(***** 2.del 3.check *****\n);printf(***** 4.modify 5.print *****\n);printf(***** 6sort *****\n);
}int main()
{Contact con;//对con空间进行清理memset(con, 0, sizeof(con));int input;do{memu();printf(Please Select:);//防止读到字符串时发生死循环input 123456;//防止下一次输入的是字符后input还是上一次的值scanf(%d, input);getchar();//输入字符串时:清空缓冲区的\nswitch (input){case ext:printf(Process Exit);break;case add:Add(con);break;case del:Del(con);break;case check:Check(con);break;case modify: Modify(con);break;case print:Print(con);break;case sort:Sort(con);break;default:printf(Select Error,Please Try Again\n);break;}}while(input);return 0;
} 但上面的通讯录无法做到 通讯录满了无法再存信息(学习动态内存管理后解决) 关闭程序时通讯录信息还存在(学习完文件操作后解决) 以上便是全部内容有问题欢迎在评论区指正感谢观看