帮初学者改代码——有多少青春可以挥霍之“c语言 多重排序”
原文:“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语言 多重排序”的更多相关文章
- 帮初学者改代码——playerc之“练习:求完数问题”(下)
前文链接:帮初学者改代码——playerc之“练习:求完数问题”(上) 再来看看be_ferfect()应该如何改. be_ferfect()函数的功能是判断number是否为完数,同时把因子对写入d ...
- 帮初学者改代码——playerc之“练习:求完数问题”(上)
原文:“练习:求完数问题” 原代码: // #include <stdio.h> #include <stdlib.h> #include <math.h> #de ...
- Magicodes.NET框架之路——让Magicodes.NET帮你编写代码
时间总是过得很快,而我几乎没有时间来安安静静的写博客和完善文档.不过总算是框架在一直前进,而我的计划是在今年年底(公历)前,让此框架成熟稳定. 在很长一段时间里,我尝试了很多我之前没有接触的技术或者没 ...
- 小米oj 不要乱改代码(并查集)
不要乱改代码 序号:#91难度:非常难时间限制:2000ms内存限制:50M 描述 最近小米公司内爆发了一种名叫"瞎改我代码就会死"的传染病. 传播方式是只要与染病者共同编辑过一 ...
- android 中的一些资源注解,让编译器帮你检查代码
android 中的一些资源注解,让编译器帮你检查代码 写方便的时候可以用注解来声明一些参数,以明确的指示参数的类型,让代码更安全.我们看到,在android源代码里大量使用了注解.我整理了一些注解如 ...
- Android开发学习之路-让注解帮你简化代码,彻底抛弃findViewById
本文主要是记录注解的使用的学习笔记,如有错误请提出. 在通常的情况下,我们在Activity中有一个View,我们要获得这个View的实例是要通过findViewById这个方法,然后这个方法返回的是 ...
- K8s集群内热改代码
1.登录到k8s master服务器 $ ssh developer@XXX.XXX.X.XXX(IP地址) 2.查看服务容器所在的节点(以xx-server为例) $ kubectl get pod ...
- FindBugs 入门——帮你减少代码中的bug数
FindBugs 入门 FindBugs 作用 开发人员在开发了一部分代码后,可以使用FindBugs进行代码缺陷的检查.提高代码的质量,同时也可以减少测试人员给你报的bug数. 代码缺陷分类 根据缺 ...
- 改代码不是很熟悉------方法上加入synchronized关键字,会有性能问题---如何改善
package com.bjpowernode.t14; import java.time.Duration;import java.time.LocalTime; public class Proc ...
随机推荐
- jquery.autocomplete.js 两种实现方法
<script type="text/javascript"> var v = 1; var stockInfoJson = [ { "name": ...
- Ubuntu deb包使用
deb是debian linus的安装格式,跟red hat的rpm非常相似,最基本的安装命令是:dpkg -i file.deb dpkg 是Debian Package的简写,是为Debian 专 ...
- Selenium2学习-037-WebUI自动化实战实例-IE浏览器显示比例问题:org.openqa.selenium.remote.SessionNotFoundException: Unexpected error launching Internet Explorer. Browser zoom level was set to 94%. It should be set to 100%
好久没有写博文了,今天在给部门新人演示 Selenium WebDriver 启动其支持的各种浏览器时,启动 IE 时总是无法打开对应的百度网址,页面如下所示:
- 与(and)&&
/与(and)&& var box = (5>3)&&(4>3); alert(box); //true 如果脸变的操作数有一个操作数不是boolean值得 ...
- Linux就这个范儿 第12章 一个网络一个世界
Linux就这个范儿 第12章 一个网络一个世界 与Linux有缘相识还得从一项开发任务说起.十八年前,我在Nucleus OS上开发无线网桥AP,需要加入STP生成树协议(SpanningTree ...
- 不连数据库List分页
package com.jpsycn.kfwggl.common.crawl; import java.util.ArrayList; import java.util.List; public cl ...
- [BS-16] 尽量将View的Opaque属性设置为YES(默认就是YES)
尽量将View的Opaque属性设置为YES(默认就是YES) UIView控件都有一个Opaque属性,如果不会更改view的透明度,那么应该将其opaque属性设置为YES.为什么要这样做呢?其实 ...
- Oracle数据库之SQL基础(二)
一.约束 ❤ 1.约束概述 约束作用: (1)定义规则 (2)确保完整性:包括数据的精确性.可靠性.以确保数据不会出错,或者尽量减少出错. 约束的类型: (1)非空约束 (2)主键约束 (3)外键约束 ...
- 手机大数据_SQL映射对象_动软_代码模板_Models
<#@ template language="c#" HostSpecific="True" #> <#@ output extension= ...
- Private strand flush not complete
当切换日志的时候,所有private strands的内容都会被flush到当前的日志中,然后日志切换才可以完成. strand是在oracle 10g中引入的新术语,和redo的latches相关. ...