第一天

1. 在《C语言深度》中,我们学习了关于冒泡法和选择法进行排序。现在就用我们学到的知识对一个学生信息进行操作,实现简单的学生信息管理系统。

我们使用简单的结构体,结构体中有四个成员:姓名(name)、性别(sex)、年龄(age)、分数(score),然后用冒泡法将学生年龄按照从大到小排列,用选择法将学生成绩按照从小到大排列,最后实现一个简单的查找函search(),实现学生姓名的查找,具体的实现如下:


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

int n = 0;

struct student
{
    charname[20];
    char sex;
    int  age;
    int score;
}*Stu;

/* 冒泡法进行排序  */
void bubble(struct student k[],int n)/*{{{*/
{
    int i=0, j=0, flag=1;
    struct student t;

    for(i=0;i<n-1&&flag==1; i++)
    {
        flag = 0;
       for(j=0; j<n-i-1; j++)
       {
           if(k[j].age<k[j+1].age)
            {
               t = k[j];
               k[j] = k[j+1];
               k[j+1] = t;
               flag = 1;
            }
        }
    }
}/*}}}*/

/* 选择法进行排序 */
void chose(struct student k[],int n)/*{{{*/
{
    int i=0,j=0,min=0;
    structstudent t;

    for(i=0; i<n-1; i++)
    {
        min = i;
        for(j=i+1; j<n; j++)
            if(k[j].score<k[min].score)
                min = j;
        if(min!= i)
        {
            t = k[min];
            k[min]=k[i];
            k[i] = t;
        }
    }
}/*}}}*/

/* 按照学生的姓名查找 */
int search(int n)/*{{{*/
{
    int i =0;
    charname[20];

   printf("请输入姓名:");

    while((name[i++]=getchar()) !='\n'&&i<((sizeof(name)/sizeof(name[0]))));
   name[i-1] ='\0';

    for(i=0;i<n; i++)
    {
       if(!strcmp(Stu[i].name,name))
        {
            printf("姓名:%s 性别:%c 年龄:%d 成绩:%d\n",Stu[i].name,Stu[i].sex,Stu[i].age,Stu[i].score);
             return 1;
        }
    }

    return 0;
}

/*}}}*/

void print(struct student stu[],int n)/*{{{*/
{
    int i = 0;

    printf("name\t\tsex\t\tage\t\tscore\n");
    for(i =0;i < n;i++)
    {
       printf("%-16s%-16c%-16d%-16d\n",stu[i].name,stu[i].sex,stu[i].age,stu[i].score);
    }
    printf("\n");
}/*}}}*/

int main()
{
    int i=0,n=0;

    printf("您想输入几个学生的信息:");
    scanf("%d",&n);
    Stu =(struct student *)malloc(n*sizeof(struct student));

   printf("\n");
   printf("请输入%d个学生的信息:\n",n);
   printf("*姓名**性别*年龄*成绩*************\n");
   for(i=0;i<n;i++)
   {      
         scanf("%s%c%d%d",Stu[i].name,&(Stu[i].sex),&(Stu[i].age),&(Stu[i].score));
         setbuf(stdin,NULL); /* 清除缓存 */
    }
   printf("学生年龄按照从大到小排列之后如下:\n");/*{{{*/
   bubble(Stu,n);
   print(Stu,n);
   printf("==============================================\n");
   printf("学生成绩按照从小到大排列之后如下:\n");
   chose(Stu,n);
   print(Stu,n);
   printf("==============================================\n");
   printf("按照学生的姓名查找学生的信息如下:\n");
   if(search(n))/*}}}*/
   {
       printf("查找成功!\n");
    }
    else
    {
       printf("查找失败!没有这个学生!\n");
    }
    free(Stu);
    Stu = NULL;
    return 0;
}

打印的结果:

  

 

在程序中,我们要注意:

  1. 选择和冒泡排序中交换的对象是一个结构体,不是一个具体的成员。
  2. 程序中用到了malloc动态分配数组,记得使用free释放内存空间。
  3. 在多次输入的时候,记得清除键盘缓存

 

2. 在上面的代码中,我们看到代码有点长,分析代码或者查找bug很麻烦,所以,我们经常把那些比较长的程序分解成一个一个的模块,然后使用makefile组合起来编译和链接,现在,我们就来将上面的代码拆分成几个模块,分别实现查找,选择排序,冒泡排序的功能。

首先我们需要构建.h的头文件,在整个头文件中,为了防止头文件发生重复,需要使用一个宏:

# ifndef   _HEAD_H  

# define   _HEAD_H

……

# endif

从字面上的意思我们就可以知道,如果下面的这些没有定义,则定义;否则,就不定义,至于前面的_HEAD_H,也可以用其他的字符表示,不过一般都是大写。省略号中的是我们要写的头文件,一般是使用到的常用的头文件,如本实例的# include <stdio.h>,# include <string.h>,# include <stdlib.h>。如果用到了结构体,还要包含结构体;在C++中,也要包含类,因为定义结构体实质上就是定义一个属于我们自己的类型。最后,还要包含一些外部使用函数的声明,如果有必要,还要包含一些全局变量,不过,这些全局变量和函数的声明都要使用extern关键字,因为函数的定义不在本文件中。最后保存,命名为.h文件即可。在本实例中,命名为head.h文件,完整的头文件有:


其次,封装每个模块。我们可以将自己所需要的功能封装成一个个模块,在一个模块中,可以有多个函数,但是,该函数一定要在刚刚的头文件有声明。这里,我们这个程序比较简单,为了讲解方便,所以我将五个函数分别封装成了五个模块,即五个文件,命名为bubble.c,chose.c,search.c,print.c,main.c。在每个文件中,给出该功能的实现,还要加上刚刚的头文件,即# include “head.h”,这里,我们只是给出冒泡排序功能bubble.c的实现,其他的,与之类似。


最后,就是makefile的编写了。其实,如果文件不大,我们可以直接使用gcc编译。如本文件中,在文件夹下,可以直接使用命令:gcc *.c,*.c表示全部的c文件,也可以使用命令:gcc bubble.c chose.c search.c print.c main.c,直接生成a.out可执行文件。写makefile主要是为了我们编译连接的方便,如果文件过多,再使用命令的话,很麻烦。所以,我们使用makefile文件进行批处理操作,说白了,makefile文件可以看做一个批处理文件,里面写上我们要执行的命令,只不过这些命令要按照一定的规则写。其实,我们也可以用一个shell脚本完成,但是shell脚本,但是,这样的话,当我们的程序中有一点点修改,执行shell脚本的时候,整个工程会全部重新编译连接一遍,而使用makefile的时候,就只更新修改过的部分,工程量小,速度快。

我们先看看本实例中的makefile是怎么写的。


在makefile中,我们先看开始部分,前三句:

       CC=gcc

       CFLAGS=-Wall–O -g

       RM=rm–rf

类似于C语言中的define宏定义,也就是为等号后面的gcc,-Wall –o –g起一个别名。这不是必须的,也可以直接使用这些命令,只是为了方便程序的浏览。用OBJ代替那些所依赖的.o文件,也可以使用*通配符代替。后面的gcc,-Wall –O –g,rm –rf这些命令大家应该清楚。gcc不用多说,表示编译链接;-Wall–O –g这不是必须的,-Wall表示不要忽略警告,-O表示优化,-g表示输出调试文件;rm –rf是一条删除命令。后面的

STU:$(OBJ)

       $(CC)$(OBJ)  –o STU

其中,STU表示目标,即最后生成的文件,也就是我们用gcc命令所生成的a.out文件,不过改了一个名字,$(OBJ),表示依赖列表,即生成前面的目标文件所需要的东西有哪些,$表示引用,CFLAGS就是前面的.o文件,下面的$(CC) $(CFLAGS) –c main.c –o main.o表示要执行的命令,具体的意思和上面差不多,$表示引用,也就是利用这些.o文件生成目标文件的命令,注意,在这一行的前面要有一个Tab制表符,这个制表符不能用空格替代,如果你的编辑器语法高亮比较智能,你会发现当你把目标和依赖写完之后,回车之后,下一行自动给出制表符,而且不用制表符和用制表符的语法高亮不一样,这是我用已经配置好的VI编辑的makefile,当我们制表符Tab键和不使用制表符的颜色是不一样的,大家看看下面的:


就会发现不同之处。但是,你会发现,如果只有这两句好像缺点什么,对了,那些main.o等.o文件是怎么生成的?下面的语句就是分别生成这些.o文件的,分析的方法和上面的类似。注意,这上面的那个目标被称为最终目标,即最后生成的可执行文件,后面的一些列操作,都是为它生成相应的依赖。在生成这些依赖之后,最后生成最终目标文件,中间会产生很多零零散散的.o文件等,所以,有了后面的那句:


即使用rm命令删除所有.o文件和目标文件,也就是相当于执行rm –rf *.o STU这条命令。对于makefile的执行,很简单,只要执行make命令就OK了,原来的文件夹中的文件:


执行make命令:


Makefile很智能,它会告诉你在哪个文件的哪一行发生什么样的错误就像上面的那样,当我们不忽略警告之后,它告诉我,在主要函数main.c文件中的第8行和第16行中的scanf函数的返回值没有做处理,不过这不影响我们的操作。如果我们再次执行make,则会告诉我最终目标已经是最新的,不需要更新:


如果我稍微修改一下代码,将bubble.c文件中的定义变量单独写,再make一下,看看结果是什么。


结果只是更新了修改过的那一个文件:


相比前面的文件夹,生成的文件多了很多.o文件和目标文件:


最后,我们执行make clean清理这些.o文件和目标文件:


好了,对于makefile我们就先讲到这,其实makefile很复杂,关于makefile大家如果有兴趣,可以看看《GNUmake 中文手册》和《跟我一起学makefile》这两本书。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值