通讯录的输入输出

在实际问题中,一组数据往往具有不同的数据类型。例如,在学生登记表中,姓名为字符型,学号为整型或字符型,年龄为整型,性别为字符型,成绩为整型或实数型别,显然,不能使用一个数组来存放这一组数据。因为数组中各个元素的类型和长度都必须一致,以便于编译系统处理。为了解决这个问题,C语言中给出了另一种构造数据类型–结构。他相当于其他高级语言中的记录。

“结构”是一种构造类型,他是由若干“成员”组成的。每一个成员可以是一个基本数据类型或者又是一个构造类型。结构是一种“构造”而成的数据类型,在说明和使用之前先定义它,也就是构造它。就像是定义函数一样。

1:结构的定义

定义一个结构的一般形式为:

struct 结构名

{

成员列表

};

2:结构类型变量的说明

说明结构变量有3中方法,以结构stu为例。

struct stu
{
    int num;
    char name[20];
    char sex;
    float score;
}; 

//说明结构变量
struct stu boy1,boy2;

说明变量boy1和boy2为stu结构类型。也可以使用宏定义用一个符号常量来表示一个结构类型,例如

#define STU struct stu
STU
{
    int num;
    char name[20];
    char sex;
    float score;
};

STU boy1,boy2;

还可以在定义结构类型的同时说明结构变量,例如:

struct stu
{
    int num;
    char name[20];
    char sex;
    float score;
} boy1,boy2;

或者是直接说明结构变量,例如

struct
{
    int num;
    char name[20];
    char sex;
    float score;
}boy1,boy2; 

注意:

如果结构变量是全局变量或为静态变量,则可以对它进行 初始化赋值。对局部或自动结构变量不能进行初始化赋值。

3:结构数组

继续使用上面stu的例子:

struct stu boys[20];

4:结构指针变量

当使用一个指针变量指向一个结构变量时,称之为结构指针变量。结构变量中的值是所指向的结构变量的首地址。通过结构指针即可访问该结构

变量,这与数组指针和函数指针的情况是相同的。结构指针变量说明的一般形式为:

struct 结构名 *结构指针变量名

例如,如果要说明一个指向stu的指针变量pstu,可写为:

struct stu *pstu;

当然,也可以在定义stu结构的时候同时说明pstu。与前面变量的声明相同。结构指针变量必须要先赋值后才能使用。赋值是把结构变量的首地址赋予该指针变量,不能把结构名赋予该指针变量,如过boy是被说明为stu类型的结构变量,则”pstu=&boy”是正确的,但是”pstu=&stu”是错误的。

结构名和结构变量是两个不同的概念,不能混淆。结构名只能表示一个结构形式,编译系统并不对他分配空间。只有当变量说明为这种类型的结构时,才对该变量分配内存空间。因此”&stu”的写法是错误的,不可能去取一个结构名的首地址。有了结构指针变量,就能更方便的访问结构变量的各个成员。

程序访问的一般形式为:(*结构指针变量).成员名 或为:

结构指针变量->成员名

例如”(*pstu).num”或者”pstu->num”。

应该注意(pstu)两侧的括号不可少,因为成员符号“.”的优先级高于”“。如果去掉括号协作pstu.num,就等效于”(pstu.num)”,这样意义就不对了。

5:结构指针变量作为函数参数

在ANSI C标准中允许使用结构变量作为函数参数进行整体传送。但是这种传送要将全部成员逐个传送,特别是成员为数组时将会使传送的时间和空间开销很大,严重的降低了程序的效率。因此最好的办法就是使用指针,即用指针变量作为函数参数进行传送,由于实参传向星灿的只是地址,从而减少了时间和空间的开销。

在本例中,假设一个通讯录由以下几项数据信息组成

数据项 类型

姓名 字符串

地址 字符串

邮政编码 字符串

电话号码 字符串

下面请看一下代码的实现:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define ZIPLEN 10
#define PHONLEN 10

struct addr{
    char *name;     /* 姓名 */
    char *address;  /* 地址 */
    char zip[ZIPLEN];   /* 邮编 */
    char phone[PHONLEN]; /* 手机号码 */
};

int readaddr(struct addr *dpt);
 int writeaddr(struct addr *dpt);

/**
 * 编制一个包含姓名,地址,邮编和电话的通讯录
 * 输入输出函数。
 */
int main()
{
    struct addr p[1];
    int i,j;

    for(i=0;readaddr(p+i);i++);
    for(j = 0;j < i;j++)
        writeaddr(p+j);

    puts("Press any key to quit...\n");
    getch();

    return 0;
}

/**
 * 向结构中的成员赋值
 *
 */
int readaddr(struct addr *dpt)
{
    int len;
    char buf[120]; //输入字符串的缓冲区

    printf("Please input the Name:\n");
    if(scanf("%s",buf)==1){
        len = strlen(buf);

        dpt->name = (char *)malloc(len+1);
        strcpy(dpt->name,buf);
    }else return 0;

    printf("Please input the Address:\n");
    if(scanf("%s",buf) == 1){
        len = strlen(buf);
        dpt->address = (char *)malloc(len+1);
        strcpy(dpt->address,buf);
    }else{
        free(dpt->name);
        return 0;
    }

    printf("Please input the Zip:\n");
    if(scanf("%s",buf) == 1){
        strncpy(dpt->zip,buf,ZIPLEN-1);
    }else{
        free(dpt->name);
        free(dpt->address);
        return 0;
    }

    printf("Please input the Phone:\n");
    if(scanf("%s",buf) == 1){
        strncpy(dpt->phone,buf,PHONLEN-1);
    }else{
        free(dpt->name);
        free(dpt->address);

        return 0;
    }

    return 1;
}

/**
 *  输出通讯录
 */
 int writeaddr(struct addr *dpt)
 {
    printf("Name        :   %s\n",dpt->name);   /* 输出姓名 */
    printf("Address     :   %s\n",dpt->address); /* 输出地址 */
    printf("Zip         :   %s\n",dpt->zip); /* 输出邮编 */
    printf("Phone       :   %s\n",dpt->phone); /* 输出手机号 */
 }

下面是运行结果:

注意:关于动态内存分配问题:

在数组中,数组的长度是预先定义好的,在整个程序中固定不变。C语言不允许动态数组类型。例如”int n;scanf(“%d”,n);int a[n];”,这是错误的。但是在实际编译过程中,往往会发生这种情况,即所需的内存空间取决于实际输入的数据,而无法预先确定。对于这种问题,用数组的办法很难解决。为了解决上述问题,C语言提供了一些内存管理函数,这些内存管理函数可以按照需要动态分配内存空间,也可以把不再使用的空间收回待用。常用的内存管理函数有3个:

1:分配内存空间函数malloc。调用形式

(类型说明符 *)malloc(size);

在内存的动态存储区中分配一块长度为”size”字节的连续区域。函数的返回值为该区域的首地址。

2:分配内存空间函数 calloc。calloc也用于分配内存空间,调用形式:

(类型说明符 *)calloc(n,size);

在内存动态存储区中分配n快长度为”size”字节的连续区域。函数的返回值为该区域的首地址。

3:释放内存空间函数free.调用形式

free(void *ptr);

释放ptr所指向的一块内存区域,ptr是一个任意类型的指针变量,他指向被释放区域的首地址。被释放区域应是由malloc或calloc函数所分配的区域。

时间: 2024-10-21 11:34:33

通讯录的输入输出的相关文章

菜鸟如何快速理解实现通讯录——静态方法

       不是有一句话叫做学生给学生当老师,学生最容易听懂明白吗?没错,我这个菜鸟给你讲讲这个通讯录项目的实现,让你更能清楚明白其中的奥秘.        这个代码没有经过过多的优化,只是为了代码能够让更多的人读懂,所以也不是最好的代码,你们可以理解了以后自己去做做优化.       代码用的知识就是C语言中最基本的操作. 项目要求: 实现一个通讯录:   通讯录可以用来存储1000个人的信息,每个人的信息包括:   姓名.性别.年龄.电话.住址 提供方法:   1. 添加联系人信息   2

蓝鸥Unity开发基础——控制台输入输出学习笔记

控制台输入输出:本节内容控制台输入输出.格式化输出 using System; namespace Lesson09{    class MainClass    {        public static void Main (string[] args)        {            /* 控制台输出语句             * 1.Console.WriteLine("");             * 2.Console.Write (" "

常用命令 - 输入输出、执行状态

输入输出 标准输入:默认为键盘,可以指定为文件. 标准输出:默认为屏幕,可以指定为文件. *****如果在终端输入时,一行不能结束,可以使用反斜杠 \ 在下一行接着输入. echo echo用于将字符串输出1 使用时如果用双引号字符串,内部的$变量将会被替换,内部的反单引号中的命令会输出``,但是转义字符不会发生转义.2 使用单引号字符串,$变量不会被替换,返单引号不会执行命令,同样不发生转义字符转义.3 不使用引号等于使用双引号. echo有三个控制选项:-n do not output th

linux输入输出重定向

http://www.cnblogs.com/chengmo/archive/2010/10/20/1855805.html 在Linux下,当一个用户进程被创建的时候,系统会自动为该进程创建三个数据流,也就是题目中所提到的这三个.那么什么是数据流呢(stream)?我们知道,一个程序要运行,需要有输入.输出,如果出错,还要能表现出自身的错误.这是就要从某个地方读入数据.将数据输出到某个地方,这就够成了数据流. 因此,一个进程初期所拥有的这么三个数据流,就分别是标准输出.标准输入和标准错误,分别

黑马程序员---C基础9【字符串的输入输出】【字符串相关函数】【指针】【指针变量初始】【二级指针】

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- [字符串的输入输出] 1.字符串输出: %s-------从给定的地址开始输出字符直到遇到\0停止: printf("%s\n",&a[3]);  printf("%s\n",a); char a[]="hellowo\0rd!"; 2.字符串输入: 从键盘上接收一个字符串,保存在以a为首地址的字符数组中 scanf("%s&

ios-私人通讯录 页面间的跳转和传值

这个demo 有多个页面 并涉及顺传和逆传 而且还有一个第三方库的导入 来实现自定义提示消息的特效 利用代理来实现页面间的传值 一个页面代表一个controller 这次  ViewController  反而一句代码都没写 // // HMContact.h // 私人通讯录 // // Created by YaguangZhu on 15/9/6. // Copyright (c) 2015年 YaguangZhu. All rights reserved. // #import <Fou

ObjectC----实现简单的通讯录(增删改查)

// Created By 郭仔 2015年04月09日21:27:50 经过两天的低迷,状态在慢慢的回归了,生活还要继续,人生还需奋斗! 祝:好人一生平安!!! ======================================================================== 题目描述: 1.创建AddressBook类. 1)使?用字典作为容器,字典的Key值为分组名(姓名?首字?母,?大写),value值为数组,数组 中存放联系?人(Person实例).(5分

FPGA编程技巧系列之输入输出偏移约束

1.   偏移约束的作用 偏移约束(Offset Constraint)用来定义一个外部时钟引脚(Pad)和数据输入输出引脚之间的时序关系,这种时序关系也被称为器件上的Pad-to-Setup或Clock-to-Out路径.这些约束对与外部元器件相连的接口十分重要,在这里,需要解释两个术语: Pad-to-Setup:也被称为OFFSET IN BEFORE约束,是用来保证外部输入时钟和外部输入数据的时序满足FPGA内部触发器的建立时间要求的.如下图TIN_BEFORE约束使得FPGA在进行DA

存储、中断、总线及输入输出系统

存储系统的基本要求: 大容量,高速度和低价格. 访问时间: 存储器从接到访存读申请,到信息被读到数据总线上所用的时间 存储周期: 连续启动一个存储体所需的时间间隔 频宽: 每秒传输的信息位数 并行主存系统: 能并行读取多个CPU字的单体多字.多体单字或多体多字的交出存储主存系统 并行主存频宽: 主存频宽与分体数m.转移概率λ的关系 中断分类: 机器校验中断 访管中断 程序性中断 外部中断 I/O中断 重新启动中断 总线: 总线是用于互连计算机.CPU.存储器.I/O端口及外部设备.远程通信设备间