修改结构体的默认对齐数
在VS这个环境下,结构体的默认对齐数是8,有时这个默认对齐数并不适用,这是我们可以对它进行修改。
#include<stdio.h>
#pragma pack(4)//将默认对齐数改成4
struck s1
{
char c1;
int i;
char c2;
};
#pragma pack()//将默认对齐数还原成环境默认的8
int main()
{
printf("%d",sizeof(s1));//得到的结果是6
return 0;
}
为什么得到6,详见我的上一条博客http://t.csdn.cn/UJShO
默认对齐数的修改让我们对结构体的使用更加灵活。
结构体传参
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;
}
上面的print1和print2函数哪个更好些?
在运用的过程中我们首选为print2函数,因为函数传参时,参数是需要压栈的,会有时间和空间上的系统开销。如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,会导致性能的下降。
结构体的应用——通讯录
这个通讯录需要包括:
人的信息:姓名、年龄、性别、地址、电话
可以放100个人的信息
功能:增加联系人、删除指定的联系人、查找指定联系人的信息、修改指定联系人的信息、显示所有联系人的信息
在编译器里我们需要创建一个头文件contact.h和两个源文件test.c、contact.c
contact.h负责函数声明
contact.c负责通讯录的实现
test.c负责通讯录的测试
要想实现这个通讯录的编写,大体可以分为以下三步:
第一步:菜单
//test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void menu()
{
printf("**************************************\n");
printf("****** 1.add 2.del ******\n");
printf("****** 3.search 4.modify ******\n");
printf("****** 5.show ******\n");
printf("****** 0.exit ******\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 0:
printf("退出通讯录\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);//当我们输入0时跳出该循环
return 0;
}
这很常规,要想使用这个通讯录,我们首先需要一个菜单,通过输入值来选择接下来要进行的操作。
第二步:创建结构体并对其进行初始化
我们将人的信息(姓名、年龄、性别、住址、手机号)放在结构体PeoInfo中,设想我们在进行增加联系人的操作时,若原本就有两个联系人,就需要从第三个位置开始存放,因此我们需要一个变量来记载此刻已经存放的联系人数量,所以有了变量sz。
为了让初始化内容更灵活,我们选择将其封装成函数InitContact,通过对这个函数的修改,我们可以对其初始化内容进行更灵活的选择。值得注意的是,这里用了地址传参。
//contact.h
#define MAX 100
#define NAME_MAX 20
#define SEX_MAX 5
#define ADDR_MAX 30
#define TELE_MAX 12
//人的信息
typedef struct PeoInfo
{
char name[NAME_MAX];//姓名
int age;//年龄
char sex[SEX_MAX];//性别
char addr[ADDR_MAX];//住址
char tele[TELE_MAX];//手机号
}PeoInfo;//重命名为PeoInfo
typedef struct Contact
{
PeoInfo data[100];//存放人的信息的
int sz;//当前已存放的信息个数
}Contact;
void InitContact(Contact* pc);//声明
//test.c
//在int input = 0的后面一行
Contact con;
//初始化通讯录
InitContact(&con);//结构体地址传参
//contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"
void InitContact(Contact* pc)
{
pc->sz = 0;
memset(pc->data, 0, sizeof(pc->data));//用memset对它进行初始化
}
第三步:完善通讯录的各个功能
以下代码均写在contact.c下
增加联系人
void AddContact(Contact* pc)
{
if (pc->sz == 100)
{
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].addr);
printf("请输入电话:>");
scanf("%s", pc->data[pc->sz].tele);
printf("添加成功\n");
pc->sz++;
}
显示联系人
void ShowContact(Contact* pc)
{
int i = 0;
printf("%-20s\t%-4s\t%-5s\t%-20s\t%-10s\n", "名字", "年龄", "性别", "住址", "手机号");
for (i = 0; i < pc->sz; i++)
{
printf("%-20s\t%-4d\t%-5s\t%-20s\t%-10s\n", pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].addr,
pc->data[i].tele);
}
}
删除指定联系人
void DelContact(Contact* pc)
{
char name[NAME_MAX] = { 0 };
if (pc->sz == 0)
{
printf("通讯录为空,无法删除\n");
return;
}
//找到要删除的人
printf("请输入要删除的人的名字:>");
scanf("%s", name);
int i = 0;
int del = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(pc->data[i].name, name) == 0)
{
del = i;
break;
}
}
//删除
for (i = del; i < pc->sz-1; i++)
{
pc->data[i] = pc->data[i + 1];//用后面一个联系人的全部信息覆盖前一个联系人的全部信息
//以达到删除的目的
}
pc->sz--;
printf("删除成功\n");
}
将“找到这个人”的一整套操作封装成一个函数,又可以把上面这段代码写成以下代码
int FindByName(const Contact* pc, char name[])
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(pc->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
void DelContact(Contact* pc)
{
char name[NAME_MAX] = { 0 };
if (pc->sz == 0)
{
printf("通讯录为空,无法删除\n");
return;
}
//找到要删除的人
printf("请输入要删除的人的名字:>");
scanf("%s", name);
int ret = FindByName(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");
}
查找联系人
void SearchContact(const Contact* pc)
{
char name[NAME_MAX];
//找到这个人
printf("请输入你想查找的人的名字:>");
scanf("%s", name);
int pos = FindByName(pc, name);
if (-1 == pos)
{
printf("要查找的人不存在\n");
return;
}
//若找到了,将这个人的信息打印出来
printf("%-20s\t%-4s\t%-5s\t%-20s\t%-10s\n", "名字", "年龄", "性别", "住址", "手机号");
printf("%-20s\t%-4d\t%-5s\t%-20s\t%-10s\n", pc->data[pos].name,
pc->data[pos].age,
pc->data[pos].sex,
pc->data[pos].addr,
pc->data[pos].tele);
}
修改联系人
void ModifyContact(Contact* pc)
{
assert(pc);
char name[NAME_MAX] = { 0 };
printf("请输入你想修改的人的名字:>");
scanf("%s", name);
int pos = FindByName(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].addr);
printf("请输入电话:>");
scanf("%s", pc->data[pos].tele);
printf("修改成功\n");
}
通讯录代码总览
//contact.h
#pragma once
#include <string.h>
#include<stdio.h>
#include<assert.h>
#define MAX 100
#define NAME_MAX 20
#define SEX_MAX 5
#define ADDR_MAX 30
#define TELE_MAX 12
//人的信息
typedef struct PeoInfo
{
char name[NAME_MAX];
int age;
char sex[SEX_MAX];
char addr[ADDR_MAX];
char tele[TELE_MAX];
}PeoInfo;
typedef struct Contact
{
PeoInfo data[MAX];//存放人的信息的
int sz;//当前已存放的信息个数
}Contact;
//初始化通讯录
void InitContact(Contact* pc);//声明
//增加联系人
void AddContact(Contact* pc);
//删除指定联系人
void DelContact(Contact* pc);
//显示通讯录中的信息
void ShowContact(const Contact* pc);
//查找联系人并显示
void SearchContact(const Contact* pc);
//修改指定联系人的信息
void ModifyContact(Contact *pc);
//contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"
void InitContact(Contact* pc)
{
assert(pc);
pc->sz = 0;
memset(pc->data, 0, sizeof(pc->data));
}
void AddContact(Contact* pc)
{
assert(pc);
if (pc->sz == MAX)
{
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].addr);
printf("请输入电话:>");
scanf("%s", pc->data[pc->sz].tele);
printf("添加成功\n");
pc->sz++;
}
void ShowContact(const Contact* pc)
{
assert(pc);
int i = 0;
printf("%-20s\t%-4s\t%-5s\t%-20s\t%-10s\n", "名字", "年龄", "性别", "住址", "手机号");
for (i = 0; i < pc->sz; i++)
{
printf("%-20s\t%-4d\t%-5s\t%-20s\t%-10s\n", pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].addr,
pc->data[i].tele);
}
}
int FindByName(const Contact* pc, char name[])
{
assert(pc);
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(pc->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
void DelContact(Contact* pc)
{
assert(pc);
char name[NAME_MAX] = { 0 };
if (pc->sz == 0)
{
printf("通讯录为空,无法删除\n");
return;
}
//找到要删除的人
printf("请输入要删除的人的名字:>");
scanf("%s", name);
int ret = FindByName(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");
}
void SearchContact(const Contact* pc)
{
assert(pc);
char name[NAME_MAX];
printf("请输入你想查找的人的名字:>");
scanf("%s", name);
int pos = FindByName(pc, name);
if (-1 == pos)
{
printf("要查找的人不存在\n");
return;
}
//若找到了,将这个人的信息打印出来
printf("%-20s\t%-4s\t%-5s\t%-20s\t%-10s\n", "名字", "年龄", "性别", "住址", "手机号");
printf("%-20s\t%-4d\t%-5s\t%-20s\t%-10s\n", pc->data[pos].name,
pc->data[pos].age,
pc->data[pos].sex,
pc->data[pos].addr,
pc->data[pos].tele);
}
void ModifyContact(Contact* pc)
{
assert(pc);
char name[NAME_MAX] = { 0 };
printf("请输入你想修改的人的名字:>");
scanf("%s", name);
int pos = FindByName(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].addr);
printf("请输入电话:>");
scanf("%s", pc->data[pos].tele);
printf("修改成功\n");
}
//test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"
void menu()
{
printf("**************************************\n");
printf("****** 1.add 2.del ******\n");
printf("****** 3.search 4.modify ******\n");
printf("****** 5.show ******\n");
printf("****** 0.exit ******\n");
printf("**************************************\n");
}
int main()
{
int input = 0;
//创建通讯录
Contact con;
//初始化通讯录
InitContact(&con);//结构体地址传参
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
AddContact(&con);
break;
case 2:
DelContact(&con);
break;
case 3:
SearchContact(&con);
break;
case 4:
ModifyContact(&con);
break;
case 5:
ShowContact(&con);
break;
case 6:
break;
case 0:
printf("退出通讯录\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
一些细节方面的补充
assert,由于使用时参数为指针,assert()可以判断指针是否非空,若为空,提醒程序员,便于找出bug。
const的使用,注意思考这步操作是否需要修改对应数值,若不需要,可用const修饰,使代码更健壮。值得注意的是,若传参有const修饰,与其对接的参数也应有const修饰,做到一一对应。
函数的封装,比如“找出这个人”这个操作,由于我们不论是删除、查找、修改都要用到这步操作,我们可以将其封装成一个函数。
后记
这篇博客只是从大体上写出了通讯录,仍有一些地方可以继续修改与完善,我会在接下来的几篇博客里慢慢补充。