高质量编程之编译警告 转载自:w57w57w57


前 言

作为程序员不但要会编程,还要编好程,即编写高质量的程序。评价程序质量的指标有很多(正确性、可靠性、有效性、可扩展性、可维护性……),用于保证软件质量的方法和技巧也非常多。本篇只讲述在编码阶段,如果通过设置编译警告级别来提高程序的质量,其目的是减少程序错误、提高程序的可维护性,进而提高软件开发效率。为了达到这个目的程序员需要:在编译程序时将编译警告级别调至最高级别!

下面主要以VC++编译器为例来说明如何设置编译警告的级别和为什么要将编译警告级别设置为最高。需要说明的是,由于本人也处于学习过程中,某些理解还不够透彻,还请读者朋友们多多指教!

1. 警告信息级别&编译警告级别

对经常编程的老手来说,编译警告级别的设置简直是小菜一碟。但我仍然坚持阐述一下如何设置,一方面是为新手指路,另外一方面是我觉得有相当一部分程序员(特别是学生)很少主动去改变程序的编译警告级别。

1.1. 警告信息级别

VC++定义了众多的编译警告消息(从C4001到C4999进行编号),并将这些警告消息根据其危害性分为4个等级:L1到L4(将编号在MSDN中查询就能看到某个警告信息的级别)。其中属于L1级别的警告危害性最大(出现这类警告的程序语句通常都意味着程序错误),L2级别的警告次之,以此类推。

1.2. 编译警告级别

编译警告级别是用来控制编译时产生警告消息数目多少的一种选项,也就是说在不同的编译警告级别下,编译器能报告的警告消息数目是不一样的。与警告信息的4个等级相对应,VC++编译器的编译警告级别包含了六个级别:None,Level 1,Level 2,Level 3,Level 4,Warning As Error。其中None表示不报告任何警告信息,Level 1表示只报告属于L1级别的警告,Level 2表示只报告L1和L2级别的警告,以此类推,Level 4能报告L1,L2,L3,L4级别的警告,而Warning As Error将所有警告信息作为编译错误来对待!需要说明的是:在VC++中,编译警告级别只对源文件起作用(包含头文件),而对目标文件(.OBJ)不起作用。

1.3. 如何指定编译警告级别

在利用VC++编译C/C++程序时,可以通过3种方式来指定编译警告级别:

1、 在继承开发环境中指定:

点击菜单栏中的“Project”菜单项,在弹出菜单中选择“Settings”,在打开的“ProjectSettings”对话框中选择C/C++标签,并在“Category”列表框中选择“General”,最后在“Warning level”列表框中选择一个编译警告级别(None,Level 1,Level 2,Level 3,Level 4),如果要选择Warnings as errors警告级别,就勾选“warning level”标签下方的“warnings as errors”复选框(见图1中的第二个红色矩形框),这时就可以把相应的警告级别的警告信息当做错误来对待。如下图所示:指定编译警告级别为Level 3,并将编译警告按编译错误对待(此时L1,L2,L3级别的警告信息均被报告,并被当做编译错误!)。值得注意的是图1中的第三个红色矩形框内的内容,它们其实对应到刚才所设定的编译警告级别:“/w3”与Level 3对应,“/wx”与warnings as errors对应,它们与在命令行指定编译警告相关。

图 1

2、 在命令行指定:

有的人喜欢在命令行进行源程序的编译,此时如何设置编译警告级别呢?其方法是在编译指令最后加上一个编译警告级别的参数,该参数可以是“/W 0”、“/W 1”、“/W 2”、“/W 3”、“/W 4”、“/W x”中的一种(注意是大写的W,W后有一个空格),它们分别与None,Level 1,Level 2,Level 3,Level 4,Warnings as errors对应。比如要在命令行以Level 3级别编译源程序main.cpp可以如下实现:cl main.cpp /W 3

3、 在源文件中指定:

前面讲述了两种指定编译警告级别的方法,其中第一种方法对当前开发环境中的所有程序有效,而第二种方法只对指定的源文件有效。这里再介绍一种更加灵活的方式:用#pragma warning(push,n)和#pragma warning(pop)配合来控制某段源程序的编译警告级别,其中n取值1,2,3,4,分别对应到Level 1,Level 2,Level 3,Level 4。注意这种方式无法指定None和warnings as errors编译警告级别!比如有一个函数f( ),其中存在L4级别的警告错误,因此你可以通过如下的方式指定该函数代码的编译警告级别为Level 3这样,就不会出现警告消息:

#pragma warning(push,3) //指定编译警告级别为Level 3

void f(/*……*/)

{

/* codes……*/

}

#pragma warning(pop) //恢复之前的编译警告级别

2. 为何使用最高级别警告

这里将通过实例来阐述使用最高编译警告级别的好处,由于水平有限,举例或许不够严谨,还望指正。

2.1. 示例程序

测试程序1

测试程序2

测试程序3

#include "stdafx.h"

int a;

int main(int argc, char* argv[])

{

int b;

int* p;

*p = b;

int* c;

return 0;

}

#include "stdafx.h"

#pragma warning(push,3)

int a;

int main(int argc, char* argv[])

{

int b;

int* p;

*p = b;

int* c;

return 0;

}

#include "stdafx.h"

#pragma warning(push,4)

int a;

int main(int argc, char* argv[])

{

int b;

int* p;

*p = b;

int* c;

return 0;

}

warning C4700: local variable 'b' used without having been initialized

warning C4700: local variable 'p' used without having been initialized

warning C4101: 'c' : unreferenced local variable

warning C4700: local variable 'b' used without having been initialized

warning C4700: local variable 'p' used without having been initialized

warning C4101: 'c' : unreferenced local variable

warning C4100: 'argv' : unreferenced formal parameter

warning C4100: 'argc' : unreferenced formal parameter

warning C4700: local variable 'b' used without having been initialized

warning C4700: local variable 'p' used without having been initialized

warning C4101: 'c' : unreferenced local variable

通过上面可以看到,测试程序1,2在编译时的警告信息是一样的: C4700和4101,原因是我的VC++6.0的默认编译器警告级别是Level 3。测试程序3编译时得到了更多的警告信息:C4100。下面对这些警告进行分析:

2.2. 分析警告&高警告编译级别的好处

2.2.1. C4700警告

其含义是:使用了未初始化的局部变量(包含普通变量和指针变量)。它属于L1级别的警告,它通常都预示着该程序将很可能发生错误。在这里的实例中,引起该警告的语句百分之99.9999……会导致程序错误:变量b的使用在运行期不会出错,但是会得到不可预知的结果,因为b是非静态(static)的局部变量,因此编译器不会对其自动初始化(关于那些变量会自动初始化,绝大多数C/C++教程都有介绍);指针变量p的使用会直接在运行期出错。

编译程序前确信编译警告级别不是“None”!假如编译时使用的编译警告级别是None,那么前面的C4700警告都不会被报告!于是程序会在运行时就会出错,并且这类错误对新手来说还很难改正。或许大多数人都觉得自己肯定不会将编译警告级别设置成None,是的,实际情况确实如此,因为大多数人(特别是学生)几乎没有关心过编译警告级别的设置问题。但是您不能不排除某些新手不小心修改了开发环境的设置后导致警告编译级别变成了“None”,您也不能排除某些自信心异常膨胀的程序员主动将编译警告级别设置成“None”!这里,再次呼吁:编译程序前确信编译警告级别不是“None”!

2.2.2. C4100警告

可能有不少人会觉得测试程序3中的两个C4100警告很陌生,这不奇怪,我用了VC++6.0近3年了,今天也是头一回发现。如果您至今还没发现这样的警告,也别气馁,这不能说明我们就不是学习编程的料,世界上的知识太多太多,咱们有不会的是理所当然的。但是,咱们不能满足于现状,要有强烈的求知欲!很多人(包括以前的我),编程时都无视一切警告,只要程序能运行,能有结果输出他就满足了,更有甚者连结果是否正确都不去探究!这样学习编程就是不对的了。

扯远了,接下来看看C4100警告:它是说main函数的两个参数没有被引用。这个警告其实是无关紧要的,它属于L4级别警告,也就是说只有当警告级别设置为Level 4时它才出现,当然它的危害性相对其他级别的警告就要低一些。我相信绝大多数人写控制台程序都没怎么用这两个参数!我再次武断的相信大多数写过控制台程序的人都没对main中这两个参数进行过处理,事实上,对这两个警告置之不理并不会造成程序错误。

那么,是不是说C4100警告就是多余的呢?非也,严格上说,上面的三个测试程序中main函数的参数都应该删除(这对应到一条编程准则:删除冗余的函数形参和局部变量)。为什么这么说呢?我们来假设这样一个场景:您在看别人写的源程序时发现某个函数的某些参数在函数体中从来没使用过。此时,你是否怀疑过该函数的正确性?我想正常人都要怀疑这个函数体中某些代码是不是被删除了(您会认为这些代码引用了那个未被引用的参数)。而事实上该函数可能100%地实现了它的所有功能,那个参数本来就是该函数的作者无意中多加上去的。假设编译警告级别不是最高的Level 4,那么这里的C4100警告就不会被报告,那么您就很难发现这些没有被使用的函数形参,从而导致这些函数让人难以理解(甚至无法理解),最终导致程序的可读性、可理解性、可维护性差!

2.2.3. C4101警告

其含义是:存在未被使用的局部变量。该警告属于L3级别。引起该警告的代码(未被使用的局部变量)也应该被删除,否则它将影响程序的可理解性和可维护性——其道理跟C4100一样!

2.2.4. 其他警告

MSDN中定义的警告从C4001到C4999。是不是我们要对这些警告都有所了解呢?非也,每一个警告都对应到特定的级别,同时也预示着出现该警告的程序语句出现错误的可能性。为了提高编程质量,最好把编译警告调节到Level 4,同时认真审查每一个警告信息。为了确定出现某个警告的程序语句出错的可能性,你可以通过对警告信息进行理解,也可以将警告代号输入MSDN查看其级别,如果是1,2,3级,那么问题通常较严重,这时必须找出代码的问题,然后进行修改。再举一例:

void f(int a,int b)

{

Int c;

c==0; // warning C4553: '==' : operator has no effect; did you intend '='?

if (a=b) { } // warning C4706: assignment within conditional expression

}

该程序段中的两个警告都与“=”或者“==”有关。C4553属于L1级别的警告,因为这样的语句毫无意义(将c于0做比较运行得到一个bool值,然后丢弃),所以它通常意味着程序员“手误”,所以其警告级别较高。

C4706是L4级别的警告,由于有时候在if语句的调价表达式中使用“=”和“==”都是有意义的,而程序员又习惯性将二者弄错,因此编译器就用警告信息来提醒程序员确认是否弄错。如果程序员的本意就是使用“=”的话,那么可以通过将条件表达式写成if((a=b)!=0)来避免产生警告信息。为了尽量避免程序员将“==”误写成“=”,当参与比较的两个数中有常量时,通常可以将常量写在左边,如将if(x==2)写成if(2==x),这样如果错误地将“==”写成了“=”就会出现编译错误:error C2106: '=' : left operand must be l-value

编译警告数量庞大,要一一举例即使不是不可能的也是非常困难的。这里不再赘述,通常我们需要把平时编程过程中遇到的问题记录在案,并时刻温习,这样就可以降低日后编程中的出错率。本篇分析C4700,C4100,C4101,C4553,C4706等常见警告的其目有三个:首先是弄懂什么情况下会产生某个警告;其次是深刻理解某个警告所预示的程序质量问题;最后是得出一个结论:为了不放过任何有可能影响程序质量的警告,请使用最高编译警告级别。

3. # pragma warning的用法

3.1. #pragma warning( warning-specifier : warning-number-list [,warning-specifier : warning-number-list...] )

其中warning-specifier包含:once,default,disable,error,而warning-number-list就是警告的编号,如前面提到的C4700。该用法的作用是:只让某些警告只出现一次(once)或者使用默认警告级别(default),或者禁用某个警告(disable),或者将警告视为错误(error)。如#pragma warning( disable : 4507 34; once : 4385; error : 164 ;default:4705)与下面三句的作用一样

#pragma warning( disable : 4507 34 ) // Disable warning messages 4507 and 34.

#pragma warning( once : 4385 ) // Issue warning 4385 only once.

#pragma warning( error : 164 ) // Report warning 164 as an error.

#pragma warning( default : 4705 ) //use default warning to 4705

注意:当警告编号大于4699时,上述语句只有写在包含出现警告的那个语句的函数外部才有效。比如要把如下的函数中的C4706警告禁止(当做错误处理、按默认处理)那么只有把#pragma warning(disable,4706)放到函数体外面才有效:

#pragma warning(disable,4706) //有效

void f(int a,int b)

{

#pragma warning(disable,4706) //无效

if (a=b) { } //warning c4796

}

3.2. #pragma warning( push[ , n ] )和#pragma warning( pop)

二者通常配合使用,前者是指定编译警告级别为1,2,3,4级,后者是恢复之前的编译警告级别。例如你的某段程序在Level 4下“不干净”,那么你可以在该程序段前使用#pragma warning( push,3 )并在该程序段后使用#pragma warning( pop )来避免不干净的程序曝光,同时也不影响其他人写的程序部分的编译警告级别。

4. 处理警告信息

针对每一个警告都要仔细审查,并选择合适的处理方式:如果是“无关紧要之警告”,可以置之不理,当然如果你觉得每次编译都弹出一些警告让人看着心烦,那么你可以在出现该“无关紧要之警告”之处将该警告disable掉,然后再将该警告打开;如果该警告指示的语句确实存在出错或者已经出错,那么该警告就是“紧要之警告”,对待这类警告只有一个办法:修改程序!将编译器的警告级别调至最高级的目的就是发现更多的“紧要之警告”当然这也会让程序员受到更多的“无关紧要之警告”的烦扰,为了提高程序的质量,受点小小烦扰还是值得的。

那么如何减少程序编译时的警告信息呢?最简单的办法是使用将警告级别设置为None(/W 0),这时什么警告都没了,呵呵。我相信您会说这是自欺欺人?是的,确实如此,程序员将所有警告都无视掉的后果就是这些警告将偷偷地报复您——出现很多很难发现的运行期错误或者逻辑错误!但是,将某些确定不会发生错误的警告disable掉是可以的。除此之外,最重要的是养成良好的编程习惯,比如说定义变量的同时就初始化,使用指针之前进行指针合法性检查等(这些优良的编程习惯可以在很多书籍中学到,如《编程精粹》、《高质量C++程序设计指南》、《More Effective C++》、《C++编程思想》……)。如果您觉一时半会学不会那些优良的编程习惯,那么最容易学会,也最容易使用的一个编程习惯就是:设置编译警告为最高级别(Level 4)。

5. 后话

作者最近在阅读《编程精粹——Microsoft编写优质无错程序秘诀》,目前还只完成了前面20页的阅读,而本篇就是作者在读该书时的感悟。本篇没有任何新颖的知识(绝大部分内容来自于MSDN Library),但是作者仍然觉得必要记下这篇读书笔记。一来是怕自己忘记这些知识,而来是可以和朋友们进行分享。

由于作者水平限制,如有不妥之处还望各位朋友指正,如有遗漏之处还望热心的朋友补充!

参考文献

1、《编程精粹——Microsoft编写优质无错程序秘诀》

2、MSDN Library – October 2001

3、《高质量C++程序设计指南》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 中文的 c 语言开发环境通常指的是对 c 语言编程语言进行编写、编译、调试和测试程序代码的一个集成开发环境(IDE)。它包括一系列编程工具,可以提供一个统一的界面,使程序员能够更轻松、更快速地开发出高质量的应用程序。 ### 回答2: 所见所得的C语言开发环境是指在编写C语言程序时,编程者所看到的开发工具和环境。 C语言是一种常用的编程语言,广泛应用于软件开发、系统编程和嵌入式设备等领域。为了方便编程者能够有效地进行C语言开发,需要有相应的开发环境。 常见的C语言开发环境有多种,其中比较典型的是集成开发环境(IDE)。IDE是一种集成了编辑器、编译器、调试器和其他开发工具的软件,能够提供全面的开发支持。 在IDE中,编程者可以通过编辑器编写C语言程序代码。编辑器具有语法高亮功能,可以使代码更易读,并提供代码自动完成、代码折叠等功能,提高编写效率。 编译器是将C语言代码转换为机器语言的工具。IDE提供了编译器的界面和设置选项,方便编程者进行编译和构建。编译器能够检查代码错误,提供编译错误和警告信息,帮助编程者快速发现和修复问题。 调试器是用于调试程序的工具,可以单步执行代码、查看变量的值,以及观察程序执行过程中的内存状态。调试器能够帮助编程者找到程序中的错误和逻辑问题。 除了集成开发环境,还有其他的C语言开发工具,如文本编辑器和命令行编译器。文本编辑器可以用来编辑代码,而命令行编译器可以通过命令行界面进行编译和构建。 总而言之,所见所得的C语言开发环境是编程者在编写C语言程序时所看到的工具和界面。这些工具和界面提供了编辑、编译和调试程序的功能,帮助编程者更加高效地进行C语言开发。 ### 回答3: 所见所得的C语言开发环境是一种以图形化界面为主的软件工具,用于开发C语言程序。它通常包含了代码编辑器,编译器,调试器,以及其他一些辅助开发工具。 首先,所见所得的C语言开发环境拥有一个直观的图形化界面,使得开发者可以通过鼠标点击和拖拽等方式进行操作,而无需手动输入命令行指令。这样的界面设计使得编写C语言程序变得更加易于上手,尤其适合那些刚刚接触C语言的初学者。 其次,所见所得的C语言开发环境集成了一个强大的代码编辑器,它为开发者提供了一系列的编辑功能,如代码自动补全、语法高亮、错误提示等。这些功能可以帮助开发者提高编码效率,减少错误。 此外,所见所得的C语言开发环境还配备了一个可直接在IDE内部编译和运行程序的编译器。这样,开发者只需要在IDE中编写完代码后点击运行按钮,即可自动执行编译和运行操作,省去了手动切换到命令行窗口的麻烦。 最后,所见所得的C语言开发环境还提供了调试器,它可以帮助开发者检查程序运行时的错误和异常。通过在IDE中设置断点、单步执行、查看变量值等调试功能,开发者可以更快速地定位并解决程序中的问题,提高代码质量。 总而言之,所见所得的C语言开发环境通过图形化界面、代码编辑器、编译器和调试器等工具的集成使用,为开发者提供了方便、快捷的开发环境,帮助他们更高效地开发C语言程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值