原文:“c语言 多重排序

原代码:

#include<stdio.h>
#include<string.h> struct A
{
char name[100];
int gread;
} stu[100]; void fact(int n)
{
int i,j,teap;
char w[100];
for(i=1;i<=n;i++)
for(j=2;j<=n;j++)
{
if(stu[j-1].gread>stu[j].gread)
teap=stu[j-1].gread,stu[j-1].gread=stu[j].gread,stu[j].gread=teap;
else
{
if(strcmp(stu[j-1].name,stu[j].name)>0)
strcpy(w,stu[j-1].name),strcpy(stu[j-1].name,stu[j].name),strcpy(stu[j].name,w);
}
}
} int main( void )
{
int n,i; scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%s",&stu[i].name);
scanf("%d",&stu[i].gread);
} fact(n); for(i=1;i<=n;i++)
printf("%s\n",stu[i].name); return 0;
}

  原代码格式很乱,可能是作者初学,也可能是因为不懂得如何排版,这里重新排了一下。

  首先,

struct A
{
char name[100];
int gread;
} stu[100];

  这明显是谭氏代码的垃圾写法,在谭书中到处都有这种垃圾写法。特点是不假思索地顺手就定义外部变量,顷刻间就把代码的结构毁于一旦。

  这个外部变量非但毁了代码的结构,而且废了fact()函数。使得fact()函数只能用于这个特定的外部数组。这种不具备一定通用性的函数是不合格的函数。

  这种写法完全是为了图省事,在声明结构体类型的同时直接定义变量,以后就不用再写一遍雍长的结构体类型的名字了。初学者很难理解这种图省事的方式有什么不好,因为感觉这很方便。这就是谭书给初学者带来的隐蔽毒害,让初学者很开心地掉入谭书挖的大坑。

  实际上这种方便就如同把卫生间里用的手纸不但放在了卫生间,同时也放到客厅、餐厅、卧室和厨房一样,无论在客厅还是在餐厅、卧室和厨房只要有了便意,立刻就可以拿到手纸。会不会也有人觉得也很方便呢?

  并不是绝对不能使用外部变量,就如同并非绝对不能使用goto语句一样。但高手对使用这种可能伤人又害己的“七伤拳”总是慎之又慎的。只有在迫不得已、有充分理由的前提下才动用这种武器。

  滥用外部变量就如同把程序的代码都写在一个main()中一样,是初学者常见的幼稚病。而从程序效率方面来说,滥用外部变量还不如把所有代码都写在main()当中。

  一般来说,变量应该定义在局部,只要可能,越局部越好。对于目前这个问题而言,stu数组应该定义在main()之中。

  此外,struct的tag——A,这名字实在是太过随意了,也是谭氏风格。

int n,i;

  这个 i 压根不应该出现在main()中。

scanf("%d",&n);

  这种赤裸裸的scanf()也是初学者代码中常见的毛病。写成下面样子比较好:

puts("请输入人数");
scanf("%d",&n);
   for(i=1;i<=n;i++)
{
scanf("%s",&stu[i].name);
scanf("%d",&stu[i].gread);
}

  这段问题较多。

  首先,数组下标应该是从0开始的,但这里数组的下标却是从1开始的;

  其次专业程序员的for语句一般这样写

   for( i =  ; i < n ; i++ )

  第三,这段代码写成一个函数为好。

fact(n);

  感觉这个函数的功能是排序,但名字居然是“fact”,实在令人莫名其妙。前面结构体的成员名gread同样令人困惑不解。英文不好,可以用汉语拼音,不丢人。一些四不像的英语才丢人。但是说起来很奇怪,有不少假洋鬼子反对用汉语拼音做标识符,但他们对那种蹩脚的、错误连篇的英文(比如老谭书中的)却从来一声不吭。那是一群很奇怪的动物,他们反对使用国家法定的母语拼音,但不反对使用垃圾甚至错误的非母语。

  这个函数的参数也不完整。它是要对数组排序,却没有数组名这个实参。这也是外部变量惹的祸。这样的函数没有什么实用价值,一点可复用性都谈不上。如果问题中另有一个数组需要排序,难道再写一个函数不成?

  由于这个函数要完成所谓的“多重排序”,所以它的比较规则要更加复杂。因此还应该有第三个参数——一个用于比较的函数作为参数。

void fact(int n)
{
int i,j,teap;
char w[100];
for(i=1;i<=n;i++)
for(j=2;j<=n;j++)
{
if(stu[j-1].gread>stu[j].gread)
teap=stu[j-1].gread,stu[j-1].gread=stu[j].gread,stu[j].gread=teap;
else
{
if(strcmp(stu[j-1].name,stu[j].name)>0)
strcpy(w,stu[j-1].name),strcpy(stu[j-1].name,stu[j].name),strcpy(stu[j].name,w);
}
}
}

  这个函数定义被写在了调用之前,从而没有写函数类型声明。这也是一种偷懒的写法,贪小便宜。无论把函数定义写在哪里都应该写函数类型声明。

int i,j,teap;

  teap这个标识符也很诡异,从来没见过这个单词。

for(i=1;i<=n;i++)

  这个属于一错再错,结果是负负得正,歪打正着。

teap=stu[j-1].gread,stu[j-1].gread=stu[j].gread,stu[j].gread=teap;
strcpy(w,stu[j-1].name),strcpy(stu[j-1].name,stu[j].name),strcpy(stu[j].name,w);

  这种写法很垃圾,是对逗号运算的滥用。它除了使代码变模糊之外,没有任何好处。不但害人,而且害己。因为不但别人难以看懂,自己也难以看明白。一旦这里有错,哭都来不及。

  此外这应该用函数完成,而不是在当地写。

         if(stu[j-1].gread>stu[j].gread)
teap=stu[j-1].gread,stu[j-1].gread=stu[j].gread,stu[j].gread=teap;
else
{
if(strcmp(stu[j-1].name,stu[j].name)>0)
strcpy(w,stu[j-1].name),strcpy(stu[j-1].name,stu[j].name),strcpy(stu[j].name,w);
}

  这个逻辑是错误的。因为按照第二个指标比较的前提是第一个指标相同。而else部分则包括了前一个元素的第一指标小于第二个元素的第一指标的情形(即stu[j-1].gread<=stu[j].gread的情况)。

  另一个让人感觉诡异的是,这里的代码只交换了结构体变量的成员,而不是交换整个结构体变量。不知道这表达的是什么逻辑。

     for(j=2;j<=n;j++)

  感觉是在写冒泡法排序,但这个“j<=n”表明冒泡法还没学懂。

  最后,main()中的

   for(i=1;i<=n;i++)
printf("%s\n",stu[i].name);

  错误同前。这里只输出name成员也不对,这根本无法测试程序是否完成了功能。此外这段也应该写成一个函数。

  下面是重构的代码。

#include<stdio.h>
#include<string.h> struct xuesheng
{
char xingming[] ;
int chengji ;
} ; void shuru( struct xuesheng [] , int );
void shuchu( struct xuesheng [] , int );
void paixu( struct xuesheng [] , int ,
int (*)( struct xuesheng , struct xuesheng ) );
void jiaohuan( struct xuesheng * , struct xuesheng * );
int dayu( struct xuesheng , struct xuesheng ); int main( void )
{
int renshu ;
struct xuesheng ban[] ; puts("请输入人数");
scanf("%d",& renshu ); shuru( ban , renshu ); paixu( ban , renshu , dayu ); shuchu( ban , renshu ); return ;
} int dayu( struct xuesheng xs1 , struct xuesheng xs2 )
{
if ( xs1.chengji > xs2.chengji )
{
return ;
} if ( xs1.chengji < xs2.chengji )
{
return -;
} if ( xs1.chengji == xs2.chengji )
{
return strcmp(xs1.xingming ,xs2.xingming );
} } void jiaohuan(struct xuesheng * p1 , struct xuesheng * p2 )
{
struct xuesheng tmp = * p1 ;
* p1 = * p2 ;
* p2 = tmp ;
} void paixu( struct xuesheng b[] , int rs , int (*dy)( struct xuesheng , struct xuesheng ) )
{
int i ; for ( i = ; i < rs ; i ++ )
{
int j ;
for ( j = ; j < rs - i ; j ++ )
{
if ( dy( b[ j ] , b[ j + ] ) == )
{
jiaohuan( &b[j] , &b[j+] );
}
}
}
} void shuchu( struct xuesheng b[] , int rs )
{
int i ;
for ( i = ;i < rs ; i++ )
{
printf("%s ", b[i].xingming);
printf("%d \n" ,b[i].chengji);
}
} void shuru ( struct xuesheng b[] , int rs )
{
int i ;
for ( i = ;i < rs ; i++ )
{
puts("姓名?");
scanf("%s",b[i].xingming);
puts("成绩?");
scanf("%d",&b[i].chengji);
}
}

帮初学者改代码——有多少青春可以挥霍之“c语言 多重排序”的更多相关文章

  1. 帮初学者改代码——playerc之“练习:求完数问题”(下)

    前文链接:帮初学者改代码——playerc之“练习:求完数问题”(上) 再来看看be_ferfect()应该如何改. be_ferfect()函数的功能是判断number是否为完数,同时把因子对写入d ...

  2. 帮初学者改代码——playerc之“练习:求完数问题”(上)

    原文:“练习:求完数问题” 原代码: // #include <stdio.h> #include <stdlib.h> #include <math.h> #de ...

  3. Magicodes.NET框架之路——让Magicodes.NET帮你编写代码

    时间总是过得很快,而我几乎没有时间来安安静静的写博客和完善文档.不过总算是框架在一直前进,而我的计划是在今年年底(公历)前,让此框架成熟稳定. 在很长一段时间里,我尝试了很多我之前没有接触的技术或者没 ...

  4. 小米oj 不要乱改代码(并查集)

     不要乱改代码 序号:#91难度:非常难时间限制:2000ms内存限制:50M 描述 最近小米公司内爆发了一种名叫"瞎改我代码就会死"的传染病. 传播方式是只要与染病者共同编辑过一 ...

  5. android 中的一些资源注解,让编译器帮你检查代码

    android 中的一些资源注解,让编译器帮你检查代码 写方便的时候可以用注解来声明一些参数,以明确的指示参数的类型,让代码更安全.我们看到,在android源代码里大量使用了注解.我整理了一些注解如 ...

  6. Android开发学习之路-让注解帮你简化代码,彻底抛弃findViewById

    本文主要是记录注解的使用的学习笔记,如有错误请提出. 在通常的情况下,我们在Activity中有一个View,我们要获得这个View的实例是要通过findViewById这个方法,然后这个方法返回的是 ...

  7. K8s集群内热改代码

    1.登录到k8s master服务器 $ ssh developer@XXX.XXX.X.XXX(IP地址) 2.查看服务容器所在的节点(以xx-server为例) $ kubectl get pod ...

  8. FindBugs 入门——帮你减少代码中的bug数

    FindBugs 入门 FindBugs 作用 开发人员在开发了一部分代码后,可以使用FindBugs进行代码缺陷的检查.提高代码的质量,同时也可以减少测试人员给你报的bug数. 代码缺陷分类 根据缺 ...

  9. 改代码不是很熟悉------方法上加入synchronized关键字,会有性能问题---如何改善

    package com.bjpowernode.t14; import java.time.Duration;import java.time.LocalTime; public class Proc ...

随机推荐

  1. The Four Stages of Recovering a Project

    If a project is in trouble, the project manager needs to work to recover it and get the schedule bac ...

  2. Java学习-021-Properties 获取配置项对应的值

    在日常的脚本编写过程中,通常会获取配置文件中的配置项,以执行相应的业务逻辑. 小二上码...若有不足之处,敬请大神指正,不胜感激! 获取配置项值的源码如下所示: /** * Get value fro ...

  3. asp.net mvc视图中嵌套分部视图

    asp.net mvc中Layout相当于webForm中母版页,分部视图相当于webForm中的用户控件. 下面例子是一个视图如何嵌套分部视图: A是分部视图,B是一般视图(A,B中的代码省略) 我 ...

  4. http://www.blogjava.net/xzclog/archive/2011/09/29/359789.html

    http://www.blogjava.net/xzclog/archive/2011/09/29/359789.html http://bbs.csdn.net/topics/380187593

  5. Linux命令行–初识Linux shell

    shell及脚本简介  GNU/Linux shell 是个交互工具,它为用户提供了启动程序.管理文件系统上的文件以及管理运行在Linux系统上的进程的途径 . shell的核心是命令行提示符 它是s ...

  6. thinkphp文章列表及删除文章

    出师不利,数据一次删完了... 教程:http://www.thinkphp.cn/topic/9757.html 首先要构造mysql数据库 模板代码 </head> <body& ...

  7. 第六篇 Integration Services:初级工作流管理

    本篇文章是Integration Services系列的第六篇,详细内容请参考原文. 简介在前几篇文章中,我们关注使用增量加载方式加载数据.在本篇文章,我们将关注使用优先约束管理SSIS控制流中的工作 ...

  8. checkbox的完美用户体验(转)

    如需查看效果-->自行建个html文件,或者-->原文:http://bbs.blueidea.com/thread-2711834-1-7.html 最常见的checkbox的使用: & ...

  9. Google File System翻译(转)

    摘要 我们设计实现了google文件系统,一个面向大规模分布式数据密集性应用的可扩展分布式文件系统.它运行在廉价的商品化硬件上提供容错功能,为大量的客户端提供高的整体性能. 尽管与现有的分布式文件系统 ...

  10. 自动检查点(Automatic Checkpointing)

    自动检查点(Automatic Checkpointing)在oracle10g,支持自动检查点调优,这样可以提高系统可用性.自动检查点调优需要开启参数fast_start_mttr_target. ...