几乎每次在代码中发现flag变量,我总是能嗅到一股馊味。不管你闻没闻到,反正我闻到了。

  在代码中,flag通常作为标志变量的名字。但问题在于,不是所有的问题或代码都需要使用这种标志变量,更不是使用标志变量一定要用flag这个名字。需要使用标志变量的问题只有一小部分而已,况且即使使用标志变量,取flag这个名字也往往是一种草率的下策。

  然而由于基础不扎实及某些书籍的误导等缘故,不少初学者往往特别善于滥用flag。几乎在任何场合他们都有能力把这个flag使用上且用得出神入化般的别扭,就如同用if语句和goto语句构造循环结构一样,甚至比这更坏。这种情况是由于不熟悉基本的代码结构所致。

另一种滥用flag的情况则是,由于最初的设计千疮百孔漏洞百出,等到发现时用flag打上丑陋的补丁。

  下面开始阅读代码,请自备口罩。

样本代码1

#include <stdio.h>
#define N 10
int main()
{
int num [N],number,flag=1,c;
char name[N][8];
/*……*/
while(flag==1)
{printf("\ninput number to look for:");
scanf("%d",&number);
search(number,num,name);
printf("continue ot not(Y/N)?");
getchar();
c=getchar();
if(c=='N'||c=='n')
flag=0;
}
/*……*/
return 0;
}

*——谭浩强 ,《C程序设计(第四版)学习辅导》,清华大学出版社,2010年7月,p92*

这段代码中的while语句是明显的缺陷,因为从这个程序段的功能来说使用do-while才是自然的表达。

#include <stdio.h>
#define N 10
int main( void )
{
int num [N],number,flag=1,c;
char name[N][8];
/*……*/
do{
printf("\ninput number to look for:");
scanf("%d",&number);
search(number,num,name);
printf("continue ot not(Y/N)?");
getchar();
c=getchar();
if(c=='N'||c=='n')
flag=0;
}
while(flag==1);
/*……*/
return 0;
}

其次flag标志变量根本没有必要,代码完全可以写成

#include <stdio.h>
#define N 10
int main( void )
{
int num [N],number,flag=1,c;
char name[N][8];
/*……*/
do{
printf("\ninput number to look for:");
scanf("%d",&number);
search(number,num,name);
printf("continue ot not(Y/N)?");
getchar();
c = getchar() ;
}
while(!(c=='N'||c=='n'));
/*……*/
return 0;
}

最后,getchar();也用得十分蹩脚,它的用意是读入前面调用scanf("%d",&number)输入一个十进制整数后输入的<CR>(回车换行)。但实际上如果程序使用者输入十进制整数后又顺手按了一个<SP>空格再回车,这个“getchar();”就会因为读入了空格而导致后面的“c = getchar() ;”读取N、n、Y、y这几个文字字符的企图遭到失败。更好的写法应该是

#include <stdio.h>
#define N 10
int main( void )
{
int num [N],number,flag=1,c;
char name[N][8];
/*……*/
do{
printf("\ninput number to look for:");
scanf("%d",&number);
search(number,num,name);
printf("continue ot not(Y/N)?");
scanf("\n%c",&c );
}
while(!(c=='N'||c=='n'));
/*……*/
return 0;
}

与“样本代码1”对比一下,不难发现样本代码1中的flag有多么拙劣。

样本代码2

#define N 10
char str[N];
int main()
{
/*……*/
int i,flag;
for(flag=1;flag==1;)
{scanf("%s",&str);
if(strlen(str)>N)
printf("string too long,input again!");
else
flag=0;
}
/*……*/
}

* ——谭浩强 ,《C程序设计(第四版)学习辅导》,清华大学出版社,2010年7月,p85*

这段代码的flag用得更加可惊可愕,它甚至不如写成功能等价的

#define N 10
char str[N];
int main(void)
{
/*……*/
int i,flag;
do {
scanf("%s",&str);
if(strlen(str)>N)
printf("string too long,input again!");
else
break;
}
while(1);
/*……*/
}

由此不难看出那个flag有多么多余。然而这个写法依旧啰嗦,更简洁的写法是:

#define N 10
char str[N];
int main(void)
{
/*……*/
do {
scanf("%s",&str);
}
while(strlen(str)>N && printf("string too long,input again!"));
/*……*/
}

这写法虽然简洁,然而依然带着样本代码中原来的毛病,这个毛病就是scanf("%s",&str);中的&str。因为str的定义是 char str[10];,而与转换格式%s相对应的参数应该是char *类型的指针,所以这个调用应该写为scanf("%s",str);。

  即便如此,代码依然是错误的。原代码企图在strlen(str)>N的情况下重新输入,这是幼稚的一厢情愿,而且有着双重的错误。从逻辑来说,strlen(str) 等于N时就已经是越界使用数组了。所以strlen(str)>N这个关系表达式不应该使用“>”运算。从实践的角度来说strlen(str)>=N时的越界已然对内存中的数据存储造成了破坏,程序以后的行为是建立在错误的基础上,最后的结果必然是错误的,至少是不可靠的。而结果不可靠,对于程序来说就是一种错误。

  那么,这段代码应该怎样写呢?由于样本代码2要解决的问题是对十个字符排序,这里的要求是输入10个字符。这根本不用写一个循环语句反复折腾,只需简单地

#define N 10
char str[N];
int main(void)
{
/*……*/
int i;
scanf("%10c" , str ) ;
/*……*/
}

即可。

  当然,也许有人会指责scanf("%10c" , str )没有用上N这个宏,这个指责确实有道理。其实用N这个宏来写scanf("%10c" , str )也并非什么难事,代码可以这样写:

#define N 10
#define FMT(x) ("%"NUM(x)"c")
#define NUM(x) #x
char str[N];
int main(void)
{
/*……*/
int i;
scanf( FMT(N) , str ) ;
/*……*/
}

转载自https://www.cnblogs.com/pmer/archive/2011/11/10/2245012.html

【转载】flag标志什么?哦,它标志代码馊了的更多相关文章

  1. 转载--Typecho install.php 反序列化导致任意代码执行

    转载--Typecho install.php 反序列化导致任意代码执行 原文链接(http://p0sec.net/index.php/archives/114/) 0x00 前言 漏洞公布已经过去 ...

  2. [转载]关于在Linux下上传代码至Github

    刚开始使用Github没多长时间,所以很多地方不太熟练,看到阿里云上有一篇文章写得不错,故转载过来. 转载自:https://www.aliyun.com/jiaocheng/122729.html ...

  3. 【转载】PDB命令行调试Python代码

    转载自这里. (博主按:PDB调试python代码和用GDB调试c++代码很类似) 你有多少次陷入不得不更改别人代码的境地?如果你是一个开发团队的一员,那么你遇到上述境地的次数比你想要的还要多.然而, ...

  4. stm32之ADC应用实例(单通道、多通道、基于DMA)-转载精华帖,最后一部分的代码是精华

    硬件:STM32F103VCT6    开发工具:Keil uVision4    下载调试工具:ARM仿真器网上资料很多,这里做一个详细的整合.(也不是很详细,但很通俗).所用的芯片内嵌3个12位的 ...

  5. [转载]Sublime Text 2 - 性感无比的代码编辑器!程序员必备神器!跨平台支持Win/Mac/Linux

    代码编辑器或者文本编辑器,对于程序员来说,就像剑与战士一样,谁都想拥有一把可以随心驾驭且锋利无比的宝剑,而每一位程序员,同样会去追求最适合自己的强大.灵活的编辑器,相信你和我一样,都不会例外. 我用过 ...

  6. 转载:用Source Insight中看Python代码

    在Source Insight中看Python代码 http://blog.csdn.net/lvming404/archive/2009/03/18/4000394.aspx SI是个很强大的代码查 ...

  7. [转载]Delphi 版 everything、光速搜索代码

    近日没啥事情,研究了一下 everything.光速搜索原理.花了一个礼拜时间,终于搞定. 废话不多说,直接上代码: unit uMFTSearchFile; { dbyoung@sina.com 2 ...

  8. [转载]Jupyter Notebook中自动补全代码

    原文地址:https://yq.aliyun.com/articles/667928 在公众号之前的文章中,已经介绍了在Jupyter Notebook中设置主题以及输出代码文件到pdf文件中,本文来 ...

  9. [转载]获取当前日期和农历的js代码

    原文地址: http://www.cnblogs.com/Gnepner/archive/2011/09/07/2169822.html 获取当前日期时间: function GetCurrentDa ...

随机推荐

  1. PTA 朋友圈 (25 分) 代码详解 (并查集)

    1.题目要求: 某学校有N个学生,形成M个俱乐部.每个俱乐部里的学生有着一定相似的兴趣爱好,形成一个朋友圈.一个学生可以同时属于若干个不同的俱乐部.根据"我的朋友的朋友也是我的朋友" ...

  2. Java GC【笔记】

    Java GC(垃圾回收机制) (PS:篇幅很长的,样式都会重新整一下,比如使用二级) 首先我们得知道,一个对象被判定为垃圾的标准是什么? 对于Java对象来说,当不被其他的对象引用的时候,就可以看作 ...

  3. Qt开发Gif播放器

    一.资源下载地址 https://www.aliyundrive.com/s/jBU2wBS8poH 本项目路径:项目->免费->Gif播放器(包含整个工程源码,vs2019打开即可编译运 ...

  4. S3C2440—10.代码重定位

    文章目录 一.启动方式 1.1 NAND FLASH 启动 1.2 NOR FLASH 启动 二. 段的概念 2.1 重定位数据段 2.2 加载地址的引出 三.链接脚本 3.1 链接脚本的引入 3.2 ...

  5. 单片机学习(八)点阵LED的使用

    目录 一.点阵LED的理论知识 1. 点阵屏的打开方式 2. LED点阵简介 3. 点阵LED的结构和操作方式 4. 74HC595模块 PPT上的简介 串行转并行的工作原理 二.编码实现 1. C5 ...

  6. python3.9 manage.py runserver 报错问题解决

    报错信息如下 You have 13 unapplied migration(s). Your project may not work properly until you apply the mi ...

  7. 《JERRY Hexo & GitHub 静态网站搭建说明》

    JERRY-Hexo-GitHub <JERRY Hexo & GitHub 静态网站搭建说明> 原创内容,转载请注明出处! 一.前言 1.1 什么是 Hexo? 一个基于 Nod ...

  8. SSM自学笔记(二)

    3.SpringMVC入门 1.Spring与Web环境集成 1.1 ApplicationContext应用上下文获取方式 应用上下文对象是通过new ClasspathXmlApplication ...

  9. Docker创建Gitea(git服务)

    背景 Gitea是流行的自托管Git服务Gogs的社区分支.gogs作者想一个人维护gogs,但是大家想一起维护.就把gogs项目fork了. 下面是gitea的介绍: https://blog.gi ...

  10. springboot如何使用事物注解方式

    1.在启动类Application中添加注解@EnableTransactionManagement import tk.mybatis.spring.annotation.MapperScan; i ...