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

news2024/11/25 4:37:40

一、结构体的声明

        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.coloradmin.cn/o/2211653.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TIM定时器(标准库)

目录 一. 前言 二. 定时器的框图 三. 定时中断的基本结构 四. TIM定时器相关代码 五. 最终现象展示 一. 前言 什么是定时器&#xff1f; 定时器可以对输入的时钟进行计数&#xff0c;并在计数值达到设定值时触发中断。 TIM定时器不仅具备基本的定时中断功能&#xff0c;而且…

【LeetCode】708. 循环有序列表的插入

目录 一、题目二、解法完整代码 一、题目 给定循环单调非递减列表中的一个点&#xff0c;写一个函数向这个列表中插入一个新元素 insertVal &#xff0c;使这个列表仍然是循环非降序的。 给定的可以是这个列表中任意一个顶点的指针&#xff0c;并不一定是这个列表中最小元素的…

2024免费mac苹果电脑清理垃圾软件CleanMyMac X4.15.8

对于苹果电脑用户来说&#xff0c;设备上积累的垃圾文件可能会导致存储空间变得紧张&#xff0c;影响电脑的性能和使用体验。尤其是那些经常下载和安装新应用、编辑视频或处理大量照片的用户&#xff0c;更容易感受到存储空间的压力。面对这种情况&#xff0c;寻找一种有效的苹…

springboot3使用Excel导入数据库数据

一、导入依赖 <!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml --><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.3.0</version></dependency> 二、…

Xilinx远程固件升级(二)——STARTUPE2原语的使用

通过&#xff08;一&#xff09;可以看出&#xff0c;对于远程固件升级实际上是通过调用flash不同区域的bit实现&#xff0c;通过golden image和update image共同保障了系统的稳定性。在项目中如果将flash的时钟直接绑定FPGA后进行约束&#xff0c;在综合编译时是无法通过的。这…

Spark:DataFrame介绍及使用

1. DataFrame详解 DataFrame是基于RDD进行封装的结构化数据类型&#xff0c;增加了schema元数据&#xff0c;最终DataFrame类型在计算时&#xff0c;还是转为rdd计算。DataFrame的结构化数据有Row&#xff08;行数据&#xff09;和schema元数据构成。 Row 类型 表示一行数据 …

MySQL 8.4修改初始化后的默认密码

MySQL 8.4修改初始化后的默认密码 &#xff08;1&#xff09;初始化mysql&#xff1a; mysqld --initialize --console &#xff08;2&#xff09;之后,mysql会生成一个默认复杂的密码&#xff0c;如果打算修改这个密码&#xff0c;可以先用旧密码登录&#xff1a; mysql -u…

Redis set类型 zset类型

set类型 类型介绍 集合类型也是保存多个字符串类型的元素的&#xff0c;但和列表类型不同的是&#xff0c;集合中 1&#xff09;元素之间是⽆序 的 2&#xff09;元素不允许重复 ⼀个集合中最多可以存储 个元素。Redis 除了⽀持 集合内的增删查改操作&#xff0c;同时还⽀持多…

【图书推荐】《R语言医学数据分析实践》

本书重点 梅俏、卢龙、丁健、张晟、黄龙、胡志坚、张琼瑶、林志刚等业内专家联袂推荐。 以公共医学数据为例&#xff0c;精选大量的实用案例&#xff0c;深入浅出地介绍统计建模分析方法。 帮助读者解决医学数据分析中遇到的实际问题。 通过实际操作引导读者入门科研论文数…

生信分析流程:从数据准备到结果解释的完整指南

介绍 生物信息学&#xff08;生信&#xff09;分析是一个复杂的过程&#xff0c;涉及从数据准备到结果解释的多个步骤。随着高通量测序技术的发展和生物数据的迅猛增长&#xff0c;了解和掌握生信分析的标准流程变得尤为重要。这不仅有助于提高分析的准确性&#xff0c;还能优…

HarmonyOS NEXT 应用开发实战(五、页面的生命周期及使用介绍)

HarmonyOS NEXT是华为推出的最新操作系统&#xff0c;arkUI是其提供的用户界面框架。arkUI的页面生命周期管理对于开发者来说非常重要&#xff0c;因为它涉及到页面的创建、显示、隐藏、销毁等各个阶段。以下是arkUI页面生命周期的介绍及使用举例。 页面的生命周期的作用 页面…

7-I2C与AHT20温湿度传感器

I2C与AHT20温湿度传感器 嵌入式领域另一种常见的通信IIC通信&#xff0c;并用其与AHT20传感器进行交互&#xff0c;获取房间的温度与湿度。 I2C有一条用于传递数据的数据线称为SDA&#xff08;Serial Data&#xff09;&#xff0c;另一条是用于提供同步时钟脉冲的时钟线SCL&am…

看图识微分与导数概念。

可建立如草图所示的局部坐标系。增量Δydy余项是草图中曲线的方程&#xff0c;微分dyydx&#xff08;是关于dx的一次函数&#xff09;是草图中切线的方程。草图形象直观地显示曲线Δy不切线dy。

安全可靠测评结果公告(2024年第1号)

大家可以选择对应的数据库&#xff0c;中央处理器&#xff0c;供参考&#xff1b;尤其是 水资源安可系统 智慧农业安可系统 智慧水利安可系统、智慧水务安可系统&#xff0c;企业安可系统 等参考使用

# 在执行 rpm 卸载软件使用 nodeps 参数时,报错 error: package nodeps is not installed 分析

在执行 rpm 卸载软件使用 nodeps 参数时&#xff0c;报错 error: package nodeps is not installed 分析 一、问题描述&#xff1a; 在执行 rpm 卸载软件使用 nodeps 参数时&#xff0c;报错 error: package nodeps is not installed 如下图&#xff1a; 二、报错分析&…

Java项目分层思路

Java项目分层思路 一、前言二、了解常见的术语1. 应用开发中使用的术语2. 建模和架构设计层面术语总结 三、如何划分1. 单个module2. 多个module 一、前言 每个人、每个开发团队的规范习惯都不太一样&#xff0c;没有固定标准&#xff0c;合适的才是最好的。 二、了解常见的术…

Python Django 查询集的延迟加载特性

Django 查询集的延迟加载特性 一、引言 在 Django 的开发过程中&#xff0c;查询集&#xff08;QuerySet&#xff09;是我们与数据库进行交互的重要工具。查询集提供了一种高效的方式来检索和操作数据库中的数据&#xff0c;且能够进行懒加载&#xff08;Lazy Loading&#x…

Gin框架教程02:AsciiJSON

什么是 AsciiJSON&#xff1f; AsciiJSON 是 Gin 框架中的一个方法&#xff0c;用于生成仅包含 ASCII 字符的 JSON。对于非 ASCII 字符&#xff08;例如汉字、特殊符号&#xff09;&#xff0c;AsciiJSON 会将其转义为 Unicode 表示&#xff08;如 \uXXXX&#xff09;&#xff…

使用CSS+SVG实现加载动画

使用CSSSVG实现加载动画 效果展示 CSS知识点 SVG元素使用SVG相关CSS属性运用 整体页面布局 <section><div class"box"><div class"loader"><svg><circle cx"40" cy"40" r"40"></circl…

vue从0开始的项目搭建(含环境配置)

一、环境准备 下载node.js 检查node.js版本 替换npm下载源 1.下载node.js: Node.js — 在任何地方运行 JavaScript (nodejs.org) 2.查看版本: windowsr输入cmd进入输入node -v命令查看版本号是否出现确认是否安装 2.替换npm下载源: npm config set registry https://reg…