C语言有关结构体的知识(后有通讯录的实现)

server/2024/10/18 17:36:58/

一、结构体的声明

        1.1 结构体的定义

        结构体是一些值的集合,这些值被称为成员变量。结构的每个成员可以是不同的类型

        1.2 结构体的声明

        这里以描述一个学生为例:

struct stu
{char name[10];//名字int age;//年龄char id[20];//学号char sex[5];//性别
};

        1.3 结构体自引用

        各位请思考:如果结构体里面需要用到他自己本身,此时的代码我们应该如何完成呢

        可以这样弄吗?

struct stu
{char name[10];struct stu s2;
};

        如果可以的话,那么sizeof(struct stu)的大小是不是就是无限的了?

        因此如果我们想实现结构体的自引用的话可以用指针(一个指针的大小是固定4/8个字节的):

struct stu
{char name[10];struct stu* s2;
};

        1.4 结构体的初始化

        其实很简单的聪明的你一定一看就会

struct stu
{char name[20];int age;char sex[5];char id[20];
};//结构体定义int main()
{struct stu s1 = { "fox", 12,"nan", "123456" };//结构体初始化return 0;
}

        也可以在定义的时候就初始化

struct stu
{char name[20];int age;char sex[5];char id[20];
}s1 = { "fox", 12, "nan", "123456" };//结构体定义及初始化

        还有结构体嵌套的初始化

struct node
{int data;struct stu s1;
}n1 = {10, { "fox", 12, "nan", "123456" } };//结构体嵌套初始化

        1.5 结构体内存对齐

        我们已经掌握了结构体的基本使用了。现在我们深入讨论一个问题:计算结构体的大小。

        这也是一个热门考点,我们举例说明:

struct s1
{char c1;char c2;int i;
};struct s2
{char c1;int i;char c2;
};int main()
{printf("%d\n", sizeof(struct s1));printf("%d\n", sizeof(struct s2));return 0;
}

        上面的代码会输出什么?

        同样都是装了两个char和一个int,为啥因为顺序不同他们的大小也不一样了呢?

        就是因为结构体内存对齐了

        那如何计算结构体的内存大小就需要清楚他的对齐规则

        1. 第一个成员在与结构体变量偏移量为0的地址处。
        2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
        对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
        VS中默认的值为8
        3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
        4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

 

        图有点丑~

        1.6 修改默认对起数

        可以通过#pragma pack()来修改默认对齐数

#pragma pack(8)//设置默认对齐数为8
struct S1
{char c1;int i;char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)//设置默认对齐数为1
struct S2
{char c1;int i;char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
int main()
{//输出的结果是什么?printf("%d\n", sizeof(struct S1));printf("%d\n", sizeof(struct S2));return 0;
}

二、通讯录的实现

        和前面几期的一样,我们用test.c来测试程序,context.c和context.h,用于实现程序功能

        2.1 大体思路

        实现通讯录,我们最少要有增、删、查、改、显示所有人的信息,排序,格式化

        通讯录中,我们需要有名字,年龄,性别,电话号,地址

        这里需要存人的信息就可以用结构体,假设我们的通讯录容量为100个人,那么我们就可以用一个大小为100的数组来管理我们的通讯录

        就可以先把程序的大体运行思路(菜单)和结构体完成

void menu()
{printf("****************************************\n");printf("******     1. add     2. del    ********\n");printf("******     3. search  4. modify ********\n");printf("******     5. show    6.sort    ********\n");printf("******     7. cln     0. exit   ********\n");printf("****************************************\n");}int main()
{int input = 0;//创建通讯录context con;//初始化通讯录init_context(&con);do{menu();printf("请选择:");scanf("%d", &input);switch (input){case ADD:add_context(&con);break;case DEL:del_context(&con);break;case SEARCH:serch_context(&con);break;case MODIFY:modify_contest(&con);break;case 5: show_context(&con);break;case SORT:sort_context(&con);break;case CLN:cln_context(&con);break;case 0 :printf("退出通讯录");break;default:printf("输入错误,请重新输入\n");break;}} while (input);return 0;
}

        头文件也可以先声明好每个函数,接下来再来实现他们

        

#define  _CRT_SECURE_NO_WARNINGS 1;#include <stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>#define MAX 100
#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 12
#define ADDR_MAX 15//声明选项
enum option
{EXIT,ADD,DEL,SEARCH,MODIFY,SHOW,SORT,CLN,
};typedef struct peo_info
{char name[NAME_MAX];int age;char sex[SEX_MAX];char tele[TELE_MAX];char addr[ADDR_MAX];
}peo_info;typedef struct context
{peo_info data[MAX];//存放人的信息int sz;//存放当前存的人的个数
}context;//初始化通讯录
void init_context(context* pc);//增加联系人
void add_context(context* pc);//显示通讯录
void show_context(context* pc);//删除联系人
void del_context(context* pc);//查找联系人
void serch_context(context* pc);//修改联系人
void modify_contest(context* pc);//排序
void sort_context(context* pc);//清空通讯录
void cln_context(context* pc);

        接下来就是一步一步把通讯录的所有功能实现了

        2.2 初始化通讯录

        初始化通讯录可以用memset函数,这样可以很方便的把我们的context全部初始化为0

//初始化通讯录
void init_context(context* pc)
{pc->sz = 0;memset(pc->data, 0, sizeof(pc->data));
}

        2.3 增加联系人

        其实就是结构体的输入和输出,注意检查pc指针是否为空指针

//增加联系人
void add_context(context* pc)
{assert(pc);if (MAX == pc->sz){printf("通讯录已满,无法存放\n");return;}//增加一个人的信息printf("请输入名字");scanf("%s", pc->data[pc->sz].name);printf("请输入年龄");scanf("%d", &pc->data[pc->sz].age);printf("请输入性别");scanf("%s", pc->data[pc->sz].sex);printf("请输入电话");scanf("%s", pc->data[pc->sz].tele);printf("请输入地址");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("添加成功\n");
}

        2.4 显示通讯录

        我们先把显示功能搞定,以便观察我们的代码是否正确

//显示通讯录
void show_context(context* pc)
{assert(pc);int i = 0;printf("%-10s\t%-4s\t%-4s\t%-13s\t%-10s\n", "姓名", "年龄", "性别", "电话", "地址");for (i = 0; i < pc->sz; i++){printf("%-10s\t%-4d\t%-4s\t%-13s\t%-10s\n", pc->data[i].name,pc->data[i].age,pc->data[i].sex,pc->data[i].tele,pc->data[i].addr);}
}

        2.5 查找函数

        后面的很多功能都需要先查找到这个人,再进行一些列的操作,因此我们先写一个查找函数(人名查找),也可以运用函数指针的知识实现人名查找、电话查找、地点查找……

int find_by_name(context* pc, char name[])
{int i = 0;for (i = 0; i < pc->sz; i++){int ret = strcmp(pc->data[i].name, name);if (0 == ret){return i;}}return -1;
}

        2.5 删除联系人

        就是先调佣查找函数找到用户想删除的那个人,再进行删除操作就可以

void del_context(context* pc)
{assert(pc);char name[NAME_MAX] = { 0 };if (0 == pc->sz){printf("通讯录为空,无法删除\n");return;}printf("请输入要删除的人的名字:");scanf("%s", name);int ret = find_by_name(pc, name);if (-1 == ret){printf("要删除的人不存在\n");return;}//删除int i = 0;for (i = ret; i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf("删除成功\n");
}

2.6 查找联系人

        同样也是先调用查找函数找到那个人,再把这个人的信息打印出来就行了

//查找联系人
void serch_context(context* pc)
{assert(pc);char name[NAME_MAX] = { 0 };printf("请输入要查找的人的名字:");scanf("%s", name);int pos = find_by_name(pc, name);if (-1 == pos){printf("要查找的人不存在\n");return;}else{printf("%-10s\t%-4s\t%-4s\t%-13s\t%-10s\n", "姓名", "年龄", "性别", "电话", "地址");printf("%-10s\t%-4d\t%-4s\t%-13s\t%-10s\n", pc->data[pos].name,pc->data[pos].age,pc->data[pos].sex,pc->data[pos].tele,pc->data[pos].addr);}
}

        2.7 修改联系人

        先用查找函数找到那个人,再进行修改操作

//修改联系人
void modify_contest(context* pc)
{printf("请输入要修改的人的名字:");char name[NAME_MAX] = { 0 };scanf("%s", name);//找人int pos = find_by_name(pc, name);if (-1 == pos){printf("要修改的人不存在\n");return;}//修改printf("请输入名字");scanf("%s", pc->data[pos].name);printf("请输入年龄");scanf("%d", &pc->data[pos].age);printf("请输入性别");scanf("%s", pc->data[pos].sex);printf("请输入电话");scanf("%s", pc->data[pos].tele);printf("请输入地址");scanf("%s", pc->data[pos].addr);printf("修改成功\n");
}

        2.8 排序通讯录

        这里我们调用库函数qsort,不熟悉qsort的朋友可以看我前几期的内容(C语言指针plus版-CSDN博客),在写上两个比大小的函数传给qsort就行

//按年龄排序
int cmp_by_age(const void* p1, const void* p2)
{return (((context*)p1)->data->age) - (((context*)p2)->data->age);
}//按名字排序
int cmp_by_name(const void* p1, const void* p2)
{return strcmp((((context*)p1)->data->name), (((context*)p2)->data->name));
}//排序通讯录
void sort_context(context* pc)
{assert(pc);if (0 == pc->sz){printf("通讯录为空,无法排序\n");return;}int input = 0;printf("0. 按年龄排序\n");printf("1. 按名字排序\n");printf("请选择:");scanf("%d", &input);if (input){printf("按名字排序\n");qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_by_name);printf("排序成功\n");return;}printf("按年龄排序\n");qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_by_age);printf("排序成功\n");
}

        2.9 清空通讯录

        其实就是再初始化一下通讯录就行了,直接调用初始化通讯录的函数就行了

//清空联系人
void cln_context(context* pc) 
{assert(pc);int input = 0;printf("确定要清空通讯录吗?\n");printf("1. YES\n");printf("0. NO\n");scanf("%d", &input);if (input){printf("清空成功\n");init_context(pc);return;}return;
}

 

        这样你就得到的一个一般般的通讯录了,下期我们弄个plus版的来~

        需要各位好心人多点点赞、评评论、这样通讯录就可以变成plusplus版的了!


http://www.ppmy.cn/server/131386.html

相关文章

VSCode 使用 EmmyLua 对lua进行调试

时间&#xff1a;2024年10月 其他&#xff1a;win10&#xff0c;EmmyLua v0.8.20 参考&#xff1a;https://blog.csdn.net/ShenHaoDeHao/article/details/140268354 有几个概念搞清楚就好理解了。一般开发中&#xff0c;我们编写的lua文件由宿主程序的来解析、执行&#xff1…

GAMES104:16 游戏引擎的玩法系统:基础AI-学习笔记

文章目录 一&#xff0c;寻路/导航系统Navigation1.1 Walkable Area1.1.1 Waypoint Network1.1.2 Grid1.1.3 Navigation Mesh1.1.4 Sparse Voxel Octree 1.2 Path Finding1.2.1 Dijkstra Algorithm迪杰斯特拉算法1.2.2 A Star&#xff08;A*算法&#xff09; 1.3 Path Smoothin…

ADS时域 连续相位观察方法

&#x1f3c6;本文收录于《全栈Bug调优(实战版)》专栏&#xff0c;主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&am…

用C++编写信息管理系统(歌单信息管理)

C语言是面向过程的编程语言&#xff0c;而C是面向对象的编程语言&#xff0c;在书写代码时风格有所不同&#xff08;也存在很多共性&#xff09;。 程序说明 本次系统程序使用的是C语言进行编写&#xff0c;主要考虑怎么实现面向对象的问题。 因为本次程序属于小型系统程序&…

基于Spring Boot的医疗病历交互系统开发指南

第2章 设计技术与开发环境 2.1 相关技术介绍 2.1.1 B/S模式分析 C/S模式主要由客户应用程序(Client)、服务器管理程序(Server)和中间件(middleware)三个部件组成。客户应用程序是系统中用户与数据组件交互。服务器程序负责系统资源&#xff0c;如管理信息数据库的有效管理&…

大数据毕业设计选题推荐-王者荣耀战队数据分析-Python数据可视化-Hive-Hadoop-Spark

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

ReactRouter快速梳理

快速开始 创建项目并安装所有依赖 npx create-react-app react-router-pro安装最新的reactrouter包 npm i react-router-dom启动项目 npm run start搭建路由&#xff08;index.js) //index.js项目的入口 从这里开始运行//react必要的两个核心包 import React from react; impo…

MokeJs使用实例

文章目录 MokeJs使用实例介绍使用安装配置文件导入配置到main.js使用 axios 发送网络请求测试&#xff08;如果不会axios&#xff0c;具体可以见上篇文章axios&#xff09;启动示例 MokeJs使用实例 介绍 使用 安装 npm install mockjs --save-dev # 或者 yarn add mockj…