结构体——简单通讯录实现
1、结构体知识回顾
1.1、结构体的声明
struct tag
{
menmber-list;
}variable-list;
描述一个学生:
struct Stu
{
char name[20];
int age;
char sex[5];
char id[20];
}; // 分号不要忘记
1.2、结构体变量的创建和初始化
#include<stdio.h>
struct Stu
{
char name[20]; // 姓名
int age; // 年龄
char sex[15]; // 性别
char id[20]; // 学号
};
int main()
{
// 按结构体成员顺序初始化
struct Stu s ={
"李四", 20, "男", "1088200066"};
printf("姓名: %s\n", s.name);
printf("年龄: %s\n", s.age);
printf("性别: %s\n", s.sex);
printf("学号: %s\n", s.id);
// 按指定的顺序初始化
struct Stu s ={
.age = 18, .name = "李四", .sex = "男", .id = "1088200066"};
printf("姓名: %s\n", s.name);
printf("年龄: %s\n", s.age);
printf("性别: %s\n", s.sex);
printf("学号: %s\n", s.id);
return 0;
}
1.3、结构特殊声明
声明结构的时候可以不完全声明。
比如:结构体在声明的时候省略结构体标签
# include<stdio.h>
int main()
{
// 定义一个匿名结构体
struct
{
int x;
int y;
}point1, point2; // point1和point2是该结构体的两个实例
// point1和point2赋值
point1.x = 10;
point1.y = 20;
point2.x = 10;
point2.y = 20;
// 输出
printf("point1:(%d %d)\n", point1.x, point2.y);
printf("point2:(%d %d)\n", point1.x, point2.y);
return 0;
}
定义匿名结构体:
- 在
struct
关键字后没有结构体命名(tag),而是直接定义了结构体成员(x 和 y). - 这种结构体只能在定义他的作用域内实现。(匿名结构体的类型没有全局或外部可见性,只有在定义它的地方才能操作该结构体的实例)
优点:
- 简洁性: 在某些情况下,使用匿名结构体可以使代码更加简洁,尤其是在只需要使用一次的情况下。
- 局部性: 匿名结构体的作用域限制在定义他的地方,避免了命名冲突。
1.4、结构的自引用
在结构体中不能包含一个类型为该结构体本身的成员,因为当结构体中再包含一个同类型的结构体变量,会使得结构体变量的大小变得无穷大,这是不合理的。但是可以包含该结构体成员的地址。
struct Node
{
int data;
struct Node* next;
};
在结构体⾃引⽤使⽤的过程中,夹杂了 typedef
对匿名结构体类型重命名,也容易引⼊问题.
问题代码:
typedef struct
{
int data;
Node* next;
}Node;
分析原因: 原理上上述代码执行完成后才完成对匿名结构体类型的重命名,但是在重命名过程中便引入了Node, 这便引起了先有鸡还是先有蛋的问题,显然是不对的。
解决方案:定义结构体不要使用匿名结构体
typedef struct Node
{
int data;
struct Node* next;
};
2、 结构体内存对齐
2.1 对齐规则
- 结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处。
- 其他成员变量要对齐到对齐数 的整数倍的地址处。
- 对齐数 = 编译器默认的对齐数和成员变量大小的较小值。
- VS中默认的对齐数是8。
- Linux中gcc没有默认的对齐数,对齐数是成员变量的自身大小。
- 结构体总大小为最大对齐数 (结构体总每个成员变量都有一个对齐数,所有对齐数中最大的)的整数倍。
- 如果嵌套了结构体的话,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数的(含嵌套结构体成员的对齐数)的整数倍。
2.2、修改默认对齐数
#pragma
这个预处理指令,可以改变编译器的默认对齐数。
# include<stdio.h>
#pragma pack(1) // 设置默认对齐数为1
struct S
{
char c1;
int i;
char c2;
};
#pragma pack() // 回复默认对齐数
int main()
{
printf("%d\n", sizeof(struct S));
return 0;
}
3、结构体传参
结构体传参最好传地址
#include<stdio,h>
struct S
{
int data[100];
int num;
}; // 不要忘记;
// 结构体传参
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;
}
4、简单通讯录的实现
要求:
实现一个通讯录
人的信息:
名字+年龄+性别+电话+地址
存放100个人的信息、增加联系人、删除指定的练习人、查找练习人、修改联系人、排序、显示连续人
为了后期维护和 查看的方便我们将该项目写成多文件的形式。
test1.c
main函数主程序
contact.c
各种函数的实现
contact.h
头文件和函数声明
test1.c
#include<stdio.h>
void menu()
{
printf("******************************************\n");
printf("******* 1.add 2.del ******\n");
printf("******* 3.search 4.modify ******\n");
printf("******* 5.show 6.sort ******\n");
printf("******* 0.exit ******\n");
printf("******************************************\n");
printf("******************************************\n");
}
int main()
{
int input = 0;
do
{
menu(); // 打印菜单
printf("请选择:>");
scanf("%d", &input);
switch(input)
{
case 1:
break;
case 2:
break:
case 3:
break:
case 4:
break;
case 5:
break:
case 6:
break;
case 0:
printf("退出通讯录\n");
break;
default: