【转载】flag标志什么?哦,它标志代码馊了
几乎每次在代码中发现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标志什么?哦,它标志代码馊了的更多相关文章
- 转载--Typecho install.php 反序列化导致任意代码执行
转载--Typecho install.php 反序列化导致任意代码执行 原文链接(http://p0sec.net/index.php/archives/114/) 0x00 前言 漏洞公布已经过去 ...
- [转载]关于在Linux下上传代码至Github
刚开始使用Github没多长时间,所以很多地方不太熟练,看到阿里云上有一篇文章写得不错,故转载过来. 转载自:https://www.aliyun.com/jiaocheng/122729.html ...
- 【转载】PDB命令行调试Python代码
转载自这里. (博主按:PDB调试python代码和用GDB调试c++代码很类似) 你有多少次陷入不得不更改别人代码的境地?如果你是一个开发团队的一员,那么你遇到上述境地的次数比你想要的还要多.然而, ...
- stm32之ADC应用实例(单通道、多通道、基于DMA)-转载精华帖,最后一部分的代码是精华
硬件:STM32F103VCT6 开发工具:Keil uVision4 下载调试工具:ARM仿真器网上资料很多,这里做一个详细的整合.(也不是很详细,但很通俗).所用的芯片内嵌3个12位的 ...
- [转载]Sublime Text 2 - 性感无比的代码编辑器!程序员必备神器!跨平台支持Win/Mac/Linux
代码编辑器或者文本编辑器,对于程序员来说,就像剑与战士一样,谁都想拥有一把可以随心驾驭且锋利无比的宝剑,而每一位程序员,同样会去追求最适合自己的强大.灵活的编辑器,相信你和我一样,都不会例外. 我用过 ...
- 转载:用Source Insight中看Python代码
在Source Insight中看Python代码 http://blog.csdn.net/lvming404/archive/2009/03/18/4000394.aspx SI是个很强大的代码查 ...
- [转载]Delphi 版 everything、光速搜索代码
近日没啥事情,研究了一下 everything.光速搜索原理.花了一个礼拜时间,终于搞定. 废话不多说,直接上代码: unit uMFTSearchFile; { dbyoung@sina.com 2 ...
- [转载]Jupyter Notebook中自动补全代码
原文地址:https://yq.aliyun.com/articles/667928 在公众号之前的文章中,已经介绍了在Jupyter Notebook中设置主题以及输出代码文件到pdf文件中,本文来 ...
- [转载]获取当前日期和农历的js代码
原文地址: http://www.cnblogs.com/Gnepner/archive/2011/09/07/2169822.html 获取当前日期时间: function GetCurrentDa ...
随机推荐
- MapReduce框架原理-Writable序列化
序列化和反序列化 序列化就是把内存中的对象,转换成字节序列(或其他数据传输协议)以便于存储(持久化)和网络传输. 反序列化就是将收到字节序列(或其他数据传输协议)或者是硬盘的持久化数据,转换成内存中的 ...
- thunderbird发送纯文本邮件
向邮件列表中发邮件时,要求邮件格式必须是纯文本格式的,在thunderbird中,邮件格式(plain text或者html格式)在[工具->账户设置->[账户名称]->通讯录]下的 ...
- Shell-08-文本处理sed
文本处理sed sed:流编辑器,过滤和替换文本 工作原理:sed命令将当前处理的行读入模式空间进行处理,处理完把结果输出,并且清空模式空间. 然后再将下一行读入模式空间进行处理输出,以此类推,直到最 ...
- Shell-06-正则表达式
正则表达式 shell正则表达式分为两种 基础正则表达式:BRE 扩展正则表达式:ERE,扩展的表达式有 + .? .| 和 () 元字符表 * 匹配0次或多次 更多请查看相关网站 http://ww ...
- Abp vNext 基础篇丨分层架构
介绍 本章节对 ABP 框架进行一个简单的介绍,摘自ABP官方,后面会在使用过程中对各个知识点进行细致的讲解. 领域驱动设计 领域驱动设计(简称:DDD)是一种针对复杂需求的软件开发方法.将软件实现与 ...
- DVWA(六):XSS-Reflected 反射型XSS全等级详解
XSS 概念: 由于web应用程序对用户的输入过滤不严,通过html注入篡改网页,插入恶意脚本,从而在用户浏览网页时,控制用户浏览器的一种攻击. XSS类型: Reflected(反射型):只是简单的 ...
- Linux下的段错误(Segmentation fault)
Linux下的段错误(Segmentation fault) 段错误是指:访问了系统分配给程序的内存空间之外起的内存空间,比如: 访问不存在的地址 访问受系统保护的地址 访问了只读内存地址 内存访问越 ...
- python关于多级包之间的引用问题
首先得明确包和模块. 包:在一个目录下存在__init__.py,那么该目录就是一个包. 模块:一个.py文件就是一个模块. 我们可以通过from 包 import 模块来引入python文件, 也可 ...
- webapp网络定位
1 <script> 2 var x=document.getElementById("demo"); 3 function getLocation() 4 { 5 i ...
- BeanUtils实现对象拷贝(三)
package beanutil; import java.lang.reflect.InvocationTargetException; import java.util.Date; import ...