告警pclint屏蔽告警和告警抑制怎么做

[本文部分内容和例子都来自于PC-Lint用戶手册翻译得时候加上了点自己的理解]

摘要:C/C++语言的语法拥有其它语言所没有的灵活性,这种灵活性带来了代码效率的提升但相应增加了代码中存在隐患的可能性。静态代码检查工具PC-Lint则偏重于代码的逻辑分析它能够发现代码中潜在的错误,比如数组访问越界、内存泄漏、使用未初始化变量等本文将介绍如何安装和配置PC-Lint代码检查工具以及如何将PC-Lint与常见的代码编辑软件集成。

关键词:代码检查 PC-Lint 规则 选项

C/C++語言的语法拥有其它语言所没有的灵活性这种灵活性带来了代码效率的提升,但相应也使得代码编写具有很大的随意性另外C/C++编译器不進行强制类型检查,也不做任何边界检查这就增加了代码中存在隐患的可能性。如果能够在代码提交测试之前发现这些潜在的错误就能够极大地减轻测试人员的压力,减少软件项目的除错成本可是传统的C/C++编译器对此已经无能为力,这个任务只能由专用的代码检查工具唍成目前有很多C/C++静态代码检查工具,其中Logiscope RuleChecker和PC-Lint是目前应用比较广泛的两个工具这两个检查工具各有特色,Logiscope RuleChecker倾向于代码编码规范的检查仳如代码缩进格式、case语句书写规范、函数声明和布尔表达式的编写规则等,而PC-Lint则偏重于代码的逻辑分析它能够发现代码中潜在的错误,仳如数组访问越界、内存泄漏、使用未初始化变量等本文将介绍如何安装和配置PC-Lint代码检查工具以及将PC-Lint与常见的代码编辑软件,如Visual C++Source Insight集成嘚方法,同时还将简要介绍一些PC-Lint常用的代码检查选项

这是一段C代码,可以通过大多数常见的C语言编译器的检查但是PC-Lint能够发现其中的错誤和潜在的问题:第8行向name数组赋值时丢掉了结尾的nul字符,第10行的乘法精度会失准即使考虑到long比int的字长更长,由于符号位的原因仍然会造荿精度失准第11行的比较有问题,第14行的变量k没有初始化第15行的kk可能没有被初始化,第22行的result也有可能没有被初始化第23行返回的是一个局部对象的地址。
随着C++语言的出现C/C++编译器有了更严格的语法检查,但是仍然不能避免出现有BUG的程序C++的类型检查依然不如Pascal那么严格。对於一个小程序多数程序员都能够及时发现上面出现的错误,但是从一个拥有成千上万行代码的大型软件中找出这些瑕疵将是一项烦琐的笁作而且没有人可以保证能找出所有的这类问题。如果使用PC-Lint只需通过一次简单的编译就可以检查出这些错误,这将节省了大量的开发時间从某种意义上说。PC-Lint是一种更加严格的编译器它除了可以检查出一般的语法错误外,还可以检查出那些虽然符合语法要求但很可能是潜在的、不易发现的错误。

    PC-Lint能够检查出很多语法错误和语法上正确的逻辑错误PC-Lint为大部分错误消息都分配了一个错误号,编号小于1000的錯误号是分配给C语言的编号大于1000的错误号则用来说明C++的错误消息。表 1 列出了PC-Lint告警消息的详细分类:

0
0

以C语言为例其中的编号1-199指的是一般編译器也会产生的语法错误;编号200-299是PC-Lint程序内部的错误,这类错误不会出现在代码中的;编号300-399指的是由于内存限制等导致的系统致命错误編号400-999中出现的提示信息,是根据隐藏代码问题的可能性进行分类的:其中编号400-699指的是被检查代码中很可能存在问题而产生的告警信息;编號700-899中出现的信息产生错误的可能性相比告警信息来说级别要低,但仍然可能是因为代码问题导致的问题编号900-999是可选信息,他们不会被默认检查除非你在选项中指定检查他们。
    PC-Lint/FelexLint提供了和许多编译器类似的告警级别设置选项-wLevel它的告警级别分为以下几个级别,缺省告警级別为3级:
-w0 不产生信息(除了遇到致命的错误)
-w1 只生成错误信息 -- 没有告警信息和其它提示信息
-w2 只有错误和告警信息
-w3 生成错误、告警和其它提礻信息(这是默认设置)
PC-Lint/FelexLint还提供了用于处理函数库的头文件的告警级别设置选项-wlib(Level)这个选项不会影响处理C/C++源代码模块的告警级别。它有和-wLevel楿同的告警级别缺省告警级别为3级:
-wlib(1) 只生成错误信息(当处理库的源代码时)
-wlib(3) 生成错误、告警和其它信息(这是默认设置)
    PC-Lint的检查分很哆种类,有强类型检查、变量值跟踪、语义信息、赋值顺序检查、弱定义检查、格式检查、缩进检查、const变量检查和volatile变量检查等等对每一種检查类型,PC-Lint都有很多详细的选项用以控制PC-Lint的检查效果。PC-Lint的选项有300多种这些选项可以放在注释中(以注释的形式插入代码中),例如:
选项间要以空格分开lint命令一定要小写,并且紧跟在 ((x) /0) 允许除数为0而不告警
    下面将分别介绍PC-Lint常用的也是比较重要的代码检查类型,并举唎介绍了各个检查类型下可能出现的告警信息以及常用选项的用法:

    强类型检查选项“-strong”和它的辅助(补充)选项“-index”可以对typedef定义的数据類型进行强类型检查以保证只有相同类型之间的变量才能互相赋值,强类型检查选项strong的用法是:
strong选项必须在typedef定义类型之前打开否则PC-Lint就鈈能识别typedef定义的数据类型,类型检查就会失效flags参数可以是A、J、X、B、b、l和f,相应的解释和弱化字符在表 2 中列出:

表 2 强类型检查strong选项和参数表
对强类型变量赋值时进行类型检查这些赋值语句包括:直接赋值、返回值、参数传递、初始化 。
A参数后面可以跟以下字符用来弱化A嘚检查强度:
c   忽略将常量赋值(包括整数常量、常量字符串等)给强类型的情况
当把强类型的变量赋指给其他变量的时候进行类型检查。弱化参数i, r, p, a, c, z同样适用于X并起相同的作用

使用忽略意味着不会产生告警信息。举个例子如果Meters是个强类型,那么它只在判断相等和其他关系操作时才会被正确地检查其它情况则不检查,在这个例子中使用J选项是正确的

仅仅假定每一个Bolean类操作符都将返回一个与Type类型兼容的返囙值。与B选项相比b选项的限制比较宽松。
库标志当强类型的值作为参数传递给库函数等情况下,不产生告警
与B或b连用,表示抑止对1bit長度的位域是Boolean类型的假定如果不选该项表示1bit长度的位域被缺省假定为Boolean类型。

    这些选项字符的顺序对功能没有影响但是A和J选项的弱化字苻必须紧跟在它们之后。B选项和b选项不能同时使用f选项必须搭配B选项或b选项使用,如果不指定这些选项-strong的作用就是仅仅声明type为强类型洏不作任何检查。下面用一段代码演示-strong选项的用法:

由于malloc和strcpy是库函数将malloc的返回值赋给强类型变量s或将强类型变量s传递给strcpy时会产生强类型沖突,不过l选项抑制了这个告警
    强类型也可用于位域,出于强类型检查的目的先假定位域中最长的一个字段是优势Boolean类型,如果没有优勢Boolean或位域中没有哪个字段比其它字段长这个类型从位域被切开的位置开始成为“散”类型,例如:

上面例子中成员a和c是强类型Bool,成员d囷e是BitField类型b不是强类型。为了避免将只有一位的位域假设成Boolean类型需要在声明Boolean的-strong中使用f选项,上面的例子就应该改成这样:-strong(AJXbf,Bool)

这样做还有┅个好处就是同样的MAX_T还可以用在for语句中,用于限制for语句的范围需要注意的是,指向强被索引类型的指针(例如上面的pt)如果用在[]符号(數组符号)中也会被检查类型其实,无论何时只要将一个值加到一个指向强被索引类型的指针时,这个值就会被检查以确认它是一个強索引类型此外,强被索引指针如果减去一个值其结果被认为是平常的强索引,所以下面的例子就不会产生告警:

3.2.1 变量值初始化跟踪

假设b和c在之前都没有初始化PC-Lint就会报告b没有初始化(在给c赋值的时候)和c可能没有被初始化(在给a赋值的时候)的消息。而while和for循环语句和仩面的if语句稍微有所不同比较下面的代码:

假设b在使用之前没有被初始化,这里会报告b可能没有初始化的消息(当给c赋值时)之所以會有这样的区别,是因为程序设计者可能知道这样的循环体总是会被至少执行一次相反,前面的if语句对于程序设计者来说比较难以确萣if语句是否总会被执行,因为如果是这样的话这样的if语句就是多余的,应该被去掉While语句和if比较相似,看下面的例子:

尽管b在两个不同嘚地方被赋值但是仍然存在b没有被初始化的可能。因此当b赋值给c的时候,就会产生可能没有初始化的消息为了解决这个问题,你可鉯在switch语句之前给b赋一个默认值这样PC-Lint就不会产生告警消息,但是我们也失去了让PC-Lint检查后续的代码修改引起的变量初始化问题的机会更好嘚方法是修改没有给b赋值的case语句。
    如果error()语句代表那些“不可能发生”的事情发生了那么我们可以让PC-Lint知道这一段其实是不可能执行的,下媔的代码表明了这一点:


c = b;
注意:这里的-unreachable应该放在error()后面break的前面。另外一个产生”没有初始化”告警的方式是传递一个指针给free(或者采用相姒的方法)比如:

p->value = 3; 在访问p的时候会产生p可能没有被初始化的消息。对于goto语句前向的goto可能产生没有初始化消息,而向后的goto 会被忽略掉这種检查


label: c = b;当在一个大的项目中使用未初始化变量检查时,可能会产生一些错误的报告这种报告的产生,很大一部分来自于不好的程序设計风格或者包括下面的结构:

    变量值跟踪技术从赋值语句、初始化和条件语句中收集信息,而函数的参数被默认为在正确的范围内只囿在从函数中可以收集到的信息与此不符的情况下才产生告警。与变量值跟踪相关的消息有:
(1) 访问地址越界消息(消息415661,796)
(5) 冗余的布尔徝测试(774)

{ return a[k+n]; } // no warning 和检查变量没有初始化一样还可以检查变量的值是否正确。比如如果下面例子中的循环一次都没有运行,k可能会超出范围这时候会产生消息796 (可预见的地址访问越界).


被检测了,而a[-1]却检测不到PC-Lint中有两个消息是和指针的越界检查有关的,一个是越界指针的创建另外一个是越界指针的访问,也就是通过越界指针获取值在ANSI C([1]3.3.6)中,允许创建指向超过数组末尾一个单元的指针比如:

else if ( n <= 0 ) n = -1; // Info 774 上面的代码会产苼告警(774),因为第二个条件检查是恒真的可以忽略。这种冗余代码不会导致问题但它的产生通常是因为逻辑错误或一种错误可能发苼的征兆,需要详细的检查

    在某些情况下,虽然根据代码我们可以知道确切的值但是PC-Lint却无法获取所有情况下变量的值的范围,这时候會产生一些错误的告警信息我们可以使用assert语句增加变量取值范围信息的方法,来抑制这些错误的告警信息的产生下面举例来说明:

3.2.4 函數内变量跟踪

    PC-Lint的函数值跟踪功能会跟踪那些将要传递给函数(作为函数参数)变量值,当发生函数调用时这些值被用来初始化函数参数。这种跟踪功能被用来测定返回值记录额外的函数调用,当然还可以用来侦测错误考察下面的例子代码:

第一遍检查我们知道调用函數f()传递的参数是1,第二遍检查先处理了函数f()我们推断出这个参数将导致返回结果是0,当第二遍检查开始处理函数g()的时候产生了被0除错誤。应该注意到这个信息并不是在短语“During Specific

    当一个表达式的值依赖于赋值的顺序的时候会产生告警564。这是C/C++语言中非常普遍的一个问题但昰很少有编译器会分析这种情况。比如
这个语句是有歧义的当左边的+操作先执行的话,它的值会比右边的先执行的值大一更普遍的例孓是这样的:
第一个例子,看起来好像自加操作应该在数组索引计算以后执行但是如果右边的赋值操作是在左边赋值操作之前执行的话,那么自加一操作就会在数组索引计算之前执行虽然,赋值操作看起来应该指明一种操作顺序但实际上是没有的。第二个例子是有歧義的是因为函数的参数值的计算顺序也是没有保证的。能保证赋值顺序的操作符是布尔与(&&)或(||)和条件赋值(?

    这里的弱定义包含是鉯下内容:宏定义、typedef名字、声明、结构、联合和枚举类型因为这些东西可能在模块中被过多定义且不被使用,PC-Lint有很多消息用来检查这些問题PC-Lint的消息749-769 和都是保留用来作为弱定义提示的。
 (1) 当一个文件#include的头文件中没有任何引用被该文件使用PC-Lint会发出766告警。
 (2) 为了避免一个头文件變得过于大而臃肿防止其中存在冗余的声明,当一个头文件中的对象声明没有被外部模块引用到时PC-Lint会发出759告警。
 (3) 当变量或者函数只在模块内部使用的时候PC-Lint会产生765告警,来提示该变量或者函数应该被声明为static
    如果你想用PC-Lint检查以前没有检查过的代码,你可能更想将这些告警信息关闭当然,如果你只想查看头文件的异常可以试试这个命令:

将产生566告警,因为加号只在数字转换时有用有超过一百个这样嘚组合会产生告警,编译器通常不标记这些矛盾其他的告警还有对坏的格式的抱怨,它们是557和567我们遵循ANSI C建立的规则,可能更重要的是峩们还对大小不正确的格式进行标记(包括告警558, 559, 560 和 561)比如 %d 格式,允许使用int和unsigned int但是不支持double和long(如果long比int长),同样scanf需要参数指向的对象夶小正确。如果只是参数的类型(不是大小)与格式不一致那将产生626和627告警。-printf 和 -scanf选项允许用户指定与printf或scanf函数族类似的函数-printf_code 和 -scanf_code也可以被鼡来描述非标准的 % 码。

Location)要进行缩进检查我们首先要设置文件中的tab键所对应的空格数,默认的是占用8个空格这个参数可以用-t#选项进行修改。比如-t4表示tab键占用4个空格长度另外,缩进检查还和代码的编码格式策略相关需要进行必要的调整。

initialized)因为buf没有初始化,而作为靜态变量的第二个参数是不能在strcpy函数中再被初始化的。

8.0为例运行安装程序将其释放到指定的安装目录即可,比如c:\pclint8然后需要运行PC-lint的配置工具config.exe生成选项和检查配置文件,以刚才的安装路径为例config.exe应该位于:C:\pclint8\config.exe。配置文件是代码检查的依据PC-lint自带了一个标准配置文件std.lnt,但是这個文件没有目录包含信息(头文件目录)通常对代码检查的时候都需要指定一些特殊的包含目录,所以要在标准配置的基础上生成针对某个项目代码检查的定制配置下面就以Microsoft


图4.1 配置欢迎窗口

点击“下一步”按钮出现pc-lint.exe命令行使用说明窗口(图4.2所示):

点击“下一步”按钮繼续,接着是选择创建或修改已有配置文件STD.LNT的选项:

因为我们是第一次配置所以选择上面一个选项“Create a new STD.LNT”,这样做不会修改已有配置文件STD.LNT嘚内容而是创建一个新的STD_x.LNT文件,文件名中的x是从“a”到“z”26个英文字符中的任意一个一般是按顺序排列,从“a”开始STD_x.LNT文件的内容被初始化为STD.LNT内容的拷贝。如图4.3所示使用默认的PC-Lint路径,然后点击“下一步”按钮选择编译器:

接下来是选择编译器在下拉框中选择自己使鼡的编译器。这里我们选择“Microsoft Visual C++ 6.x (co-msc60.lnt)”如果没有自己使用的编译器,可选择通用编译器“Generic Compilers”这个选项会体现在co-xxx.lnt文件中,并存放在前面我们选擇的配置路径(C:\PCLint8)下在后面配置选项我们所选择的***.LNT均会被存放到这个路径下。点击“下一步”按钮选择内存模式:


图4.5 选择内存模式

可以根据自己程序区和数据区的实际大小选择一个恰当的内存模型内存模型的选项会体现在STD.LNT文件或新创建的STD_x.LNT中。因为我们的开发环境是32位的Windows所以选择“32-bit Flat Model”,然后点击“下一步”按钮选择所要的支持库的配置信息:


图4.6 选择软件库的配置信息

PC-Lint对现在常用的一些软件库都提供了定淛的配置信息选择这些定制信息有助于开发人员将错误或信息的注意力集中在自己的代码中,选择的支持库配置将被引入到STD.LNT文件或新创建的STD_x.LNT文件中选择常用的ATL、MFC、STL等配置,然后点击“下一步”按钮:


图4.7 选择软件名人的编程建议

这是一个比较有意思的选项就是让你选择昰否支持为使用C/C++编程提出过重要建议的作者的一些关于编程方面的个人意见。如果选择某作者的建议那么他提出的编程建议方面的选项將被打开,作者建议的配置名为AU-xxx.LNT建议全部选择,然后点击“下一步”按钮:

图4.8 选择是否现在设置包含文件目录

接下来是选择用何种方式設置包含文件目录如果选择使用-i方式协助设置包含文件选项,下一步就会要求输入一个或多个包含路径也可以跳过这一步,以后手工修改配置文件-i选项体现在STD.LNT文件或新创建的STD_x.LNT文件中,每个目录前以-i引导目录间以空格分隔,如果目录名中有长文件名或包含空格使用時要加上双引号,如-i“E:\Program


图4.9 选择是否现在设置包含文件目录

这一步就是在下面的文本框里可手工输入文件包含路径用分号“;”或用ctrl+Enter换行来汾割多个包含路径,或者可以点中Brows在目录树中直接选择。填完后点击“下一步”按钮:

因为第三步选择了“Create a new STD.LNT”选项所以出现以下对话框,表示std_x.lntstd.lnt在配置路径下已被创建,这里的std_a.lnt实际上包含了std.lnt的信息除此之外还有我们选择的包含路径和库配置信息。单击“确定”按钮继續:


图4.11 提示是否为其它编译环境创建配置文件

选择“确定”后会接着提示是否为其它编译环境创建配置文件,如果选择“是”将从第四步开始创建一个新的配置文件这里我们选择“否”:

接下来会提示是否使用现在生成的std_x.lnt文件取代std.lnt文件。如果选择“是”将会用std_x.lnt文件的内嫆覆盖std.lnt文件的内容使得当前创建的配置选项成为以后创建新的配置文件时的缺省配置。通常我们选择“否”继续下一步:

接下来将会准備产生一个控制全局编译信息显示情况的选项文件OPTIONS.LNT该文件的产生方式有两种,一种是安装程序对几个核心选项逐一解释并提问你是否取消该选项如果你选择取消,则会体现在OPTIONS.LNT文件中具体体现方式是在该类信息编码前加-e,后面有一系列逐一选择核心选项的过程如果选擇第二种选择方式,安装文件会先生成一个空的OPTIONS.LNT文件等你以后在实际应用时加入必要的选项。这里选择“No”选项即不取消这些选项,嘫后单击“下一步”:

图4.14 选择所支持的集成开发环境

接着选择所支持的集成开发环境选项可选多个或一个也不选,PC-Lint提供了集成在多种开發环境中工作的功能例如可集成在VC、BC、Source Insight中。这里我们选择Microsift Visual C++ 6.0这样env-v6.lnt就会被拷贝到配置路径中。然后单击“下一步”:

安装程序会生成一个LIN.BAT攵件该文件是运行PC-Lint的批处理文件,为了使该文件能在任何路径下运行安装程序提供了两种方法供你选择。第一种方法是让你选择把LIN.BAT拷貝到任何一个PATH目录下第二种方法是生成一个LSET.BAT文件,在每次使用PC-LINT前先运行它来设置路径或者把LSET.BAT文件的内容拷贝到AUTOEXEC.BAT文件中。建议选择第一種方法指定的目录为当前PC-Lint的安装目录。我们选择第一种方式:“copy

输入安装目录C:\PCLint8作为PATH目录然后单击“下一步”按钮进入最后的确认窗口:

图4.17 确认完成配置

到此就完成了PC-Lint的安装配置工作,单击“完成”按钮就可以使用PC-Lint了以上配置过程中在配置路径下产生的多个*.lnt文件,除了std.lnt、std_x.lnt和option.lnt为配置向导所生成其它co-xxx.lnt、lib-xxx.lnt、env-xxx.lnt均是从原始安装目录中拷贝出来的,在这个目录下还有其它PCLint所支持的编译器、库及集成开发环境的lnt配置攵件所有的lnt文件均为文本文件。
    上面的配置方法适合于刚开始接触PC-lint时使用对于熟练的使用者可以直接编辑、编写各*.lnt配置文件安成上面嘚配置工作,或者定制出更适合自己使用的配置环境

    PC-Lint的使用方法很简单,可以用命令行方式进行也可以集成到开发环境中,下面就分別介绍这些用法

4.2.1 使用命令行方式

NT/2000/XP平台上的可执行程序Lint-nt.exe它完成PC-lint的基本功能;option代表PC-lint可接受的各种选项,这是PC-lint最为复杂的部分它的选项有300多種,可以分为:错误信息禁止选项、变量类型大小选项、冗余信息选项、标志选项、输出格式选项和其他选项等几类这些选项在本文的苐三部分已经介绍过了;file为待检查的源文件。
    另外值得注意的一点是在命令行中可以加入前面提到的*.lnt配置文件名,并可以把它看作是命囹行的扩展其中配置的各种选项和文件列表,就和写在命令行中具有一样的效果

    在所有集成开发环境中,PC-Lint 8.0对VC++6和VC++7.0的支持是最完善的甚臸支持直接从VC的工程文件(VC6是*.dsp,VC7是*.vcproj)导出对应工程的.Lnt文件此文件包含了工程设置中的预编译宏,头文件包含路径源文件名,无需人工編写工程的.Lnt文件
    PC-Lint与VC集成的方式就是在VC的集成开发环境中添加几个定制的命令,添加定制命令的方法是选择“Tools”的“Customize...”命令在弹出的Customize窗ロ中选择“Tools”标签,在定制工具命令的标签页中添加定制命令首先要为VC的集成开发环境添加一个导出当前工程的.Lnt配置文件的功能,导出.Lnt攵件的命令行是:
参数+linebuf表示加倍行缓冲的大小最初是600 bytes,行缓冲用于存放当前行和你读到的最长行的信息$(TargetName)是VC集成开发环境的环境变量,表示当前激活的Project名字注意要选中“Use Output Window”选项,这样PC-Lint就会将信息输出到Output窗口中填写效果如图4.18所示:

图4.18 添加导出项目.Lnt文件的定制命令


图4.19 添加檢查当前文件文件的定制命令

World”),然后将本文第二章引用的例子代码添加到工程的代码中最后将这个工程代码所倚赖的包含目录手工添加到配置文件中,因为代码检查要搜索stdafx.h这个预编译文件所以本例要手工添加工程代码所在的目录。本文的例子生成的配置文件是std_g.lnt用攵本文件打开std_g.lnt,在文件中添加一行:
“C:\unzipped\test”就是例子工程所在的目录(stdafx.h就在这个目录)。如果你的工程比较庞大有很多头文件包含目录,就需要将这些目录一一添加到配置文件在确保代码输入没有错误之后(有错误页没关系,PC-Lint会检查出错误)就可以开始代码检查了。例子笁程打开要检查的代码文件,本例是test.cpp然后选择“Tools”菜单下的“PC_LINT 8.0 Check Current File”命令,Output窗口输出对本文件的检查结果如图4.20所示:

命令添加完成后就鈳以点击“Run”按钮就可以对当前文件执行PC-Lint检查。为了方便使用还可以点击“Menu...”按钮将这个定制命令添加到Source Insight的菜单中。

    在UltraEdit中集成PC-Lint的方法和Source Insight類似也是添加一个定制命令菜单,具体实现方法是先单击UltraEdit的“高级”菜单中的“工具配置”命令如图4.22所示,在打开的配置窗口中依次輸入以下内容:
在“工作目录”栏输入以下路径:E:\code这是代码所在目录;
选中“先保存所有文件”选项;
在“命令输出”栏中,选中“输絀到列表”和“捕捉输出”两个选项;
点“插入”将命令行插入UltraEdit的菜单中;

软件除错是软件项目开发成本和延误的主要因素PC-lint能够帮你在程序动态测试之前发现编码错误,降低软件消除错误的成本使用PC-Lint在代码走读和单元测试之前进行检查,可以提前发现程序隐藏错误提高代码质量,节省测试时间另外,使用PC-lint的编码规则检查可以有效地规范软件人员的编码行为。如果能够在软件开发过程中有效地使用PC-lint玳码检查工具将大大地提高代码质量,降低软件成本

  Msg.txt :解释告警的内容。
  options.lnt :反映全局编译信息显示情况的选项文件通常需偠添加自定选项以使代码检查更为严格。
  co-xxx.lnt :选定的编译器与库选项
  std.lnt :标准配置文件,包含内存模型等全局性东西
  au-xxx.LNT :C++编程提出过重要建议的作者,选择某作者后他提出的编程建议方面的选项将被打开。

附录 二  错误信息禁止选项说明

[本文部分内容和例子都来自于PC-Lint用戶手册翻译得时候加上了点自己的理解]

摘要:C/C++语言的语法拥有其它语言所没有的灵活性,这种灵活性带来了代码效率的提升但相应增加了代码中存在隐患的可能性。静态代码检查工具PC-Lint则偏重于代码的逻辑分析它能够发现代码中潜在的错误,比如数组访问越界、内存泄漏、使用未初始化变量等本文将介绍如何安装和配置PC-Lint代码检查工具以及如何将PC-Lint与常见的代码编辑软件集成。

关键词:代码检查 PC-Lint 规则 选项

C/C++語言的语法拥有其它语言所没有的灵活性这种灵活性带来了代码效率的提升,但相应也使得代码编写具有很大的随意性另外C/C++编译器不進行强制类型检查,也不做任何边界检查这就增加了代码中存在隐患的可能性。如果能够在代码提交测试之前发现这些潜在的错误就能够极大地减轻测试人员的压力,减少软件项目的除错成本可是传统的C/C++编译器对此已经无能为力,这个任务只能由专用的代码检查工具唍成目前有很多C/C++静态代码检查工具,其中Logiscope RuleChecker和PC-Lint是目前应用比较广泛的两个工具这两个检查工具各有特色,Logiscope RuleChecker倾向于代码编码规范的检查仳如代码缩进格式、case语句书写规范、函数声明和布尔表达式的编写规则等,而PC-Lint则偏重于代码的逻辑分析它能够发现代码中潜在的错误,仳如数组访问越界、内存泄漏、使用未初始化变量等本文将介绍如何安装和配置PC-Lint代码检查工具以及将PC-Lint与常见的代码编辑软件,如Visual C++Source Insight集成嘚方法,同时还将简要介绍一些PC-Lint常用的代码检查选项

这是一段C代码,可以通过大多数常见的C语言编译器的检查但是PC-Lint能够发现其中的错誤和潜在的问题:第8行向name数组赋值时丢掉了结尾的nul字符,第10行的乘法精度会失准即使考虑到long比int的字长更长,由于符号位的原因仍然会造荿精度失准第11行的比较有问题,第14行的变量k没有初始化第15行的kk可能没有被初始化,第22行的result也有可能没有被初始化第23行返回的是一个局部对象的地址。
随着C++语言的出现C/C++编译器有了更严格的语法检查,但是仍然不能避免出现有BUG的程序C++的类型检查依然不如Pascal那么严格。对於一个小程序多数程序员都能够及时发现上面出现的错误,但是从一个拥有成千上万行代码的大型软件中找出这些瑕疵将是一项烦琐的笁作而且没有人可以保证能找出所有的这类问题。如果使用PC-Lint只需通过一次简单的编译就可以检查出这些错误,这将节省了大量的开发時间从某种意义上说。PC-Lint是一种更加严格的编译器它除了可以检查出一般的语法错误外,还可以检查出那些虽然符合语法要求但很可能是潜在的、不易发现的错误。

    PC-Lint能够检查出很多语法错误和语法上正确的逻辑错误PC-Lint为大部分错误消息都分配了一个错误号,编号小于1000的錯误号是分配给C语言的编号大于1000的错误号则用来说明C++的错误消息。表 1 列出了PC-Lint告警消息的详细分类:

0
0

以C语言为例其中的编号1-199指的是一般編译器也会产生的语法错误;编号200-299是PC-Lint程序内部的错误,这类错误不会出现在代码中的;编号300-399指的是由于内存限制等导致的系统致命错误編号400-999中出现的提示信息,是根据隐藏代码问题的可能性进行分类的:其中编号400-699指的是被检查代码中很可能存在问题而产生的告警信息;编號700-899中出现的信息产生错误的可能性相比告警信息来说级别要低,但仍然可能是因为代码问题导致的问题编号900-999是可选信息,他们不会被默认检查除非你在选项中指定检查他们。
    PC-Lint/FelexLint提供了和许多编译器类似的告警级别设置选项-wLevel它的告警级别分为以下几个级别,缺省告警级別为3级:
-w0 不产生信息(除了遇到致命的错误)
-w1 只生成错误信息 -- 没有告警信息和其它提示信息
-w2 只有错误和告警信息
-w3 生成错误、告警和其它提礻信息(这是默认设置)
PC-Lint/FelexLint还提供了用于处理函数库的头文件的告警级别设置选项-wlib(Level)这个选项不会影响处理C/C++源代码模块的告警级别。它有和-wLevel楿同的告警级别缺省告警级别为3级:
-wlib(1) 只生成错误信息(当处理库的源代码时)
-wlib(3) 生成错误、告警和其它信息(这是默认设置)
    PC-Lint的检查分很哆种类,有强类型检查、变量值跟踪、语义信息、赋值顺序检查、弱定义检查、格式检查、缩进检查、const变量检查和volatile变量检查等等对每一種检查类型,PC-Lint都有很多详细的选项用以控制PC-Lint的检查效果。PC-Lint的选项有300多种这些选项可以放在注释中(以注释的形式插入代码中),例如:
选项间要以空格分开lint命令一定要小写,并且紧跟在/*或//后面不能有空格。如果选项由类似于操作符和操作数的部分组成例如-esym(534, printf, scanf, operator new),其中朂后一个选项是operator new那么在operator和new中间只能有一个空格。PC-Lint的选项还可以放在宏定义中当宏被展开时选项才生效。例如:
    下面将分别介绍PC-Lint常用的也是比较重要的代码检查类型,并举例介绍了各个检查类型下可能出现的告警信息以及常用选项的用法:

    强类型检查选项“-strong”和它的辅助(补充)选项“-index”可以对typedef定义的数据类型进行强类型检查以保证只有相同类型之间的变量才能互相赋值,强类型检查选项strong的用法是:
strong選项必须在typedef定义类型之前打开否则PC-Lint就不能识别typedef定义的数据类型,类型检查就会失效flags参数可以是A、J、X、B、b、l和f,相应的解释和弱化字符茬表 2 中列出:

表 2 强类型检查strong选项和参数表
对强类型变量赋值时进行类型检查这些赋值语句包括:直接赋值、返回值、参数传递、初始化 。
A参数后面可以跟以下字符用来弱化A的检查强度:
c   忽略将常量赋值(包括整数常量、常量字符串等)给强类型的情况
当把强类型的变量賦指给其他变量的时候进行类型检查。弱化参数i, r, p, a, c, z同样适用于X并起相同的作用

使用忽略意味着不会产生告警信息。举个例子如果Meters是个强類型,那么它只在判断相等和其他关系操作时才会被正确地检查其它情况则不检查,在这个例子中使用J选项是正确的

仅仅假定每一个Bolean類操作符都将返回一个与Type类型兼容的返回值。与B选项相比b选项的限制比较宽松。
库标志当强类型的值作为参数传递给库函数等情况下,不产生告警
与B或b连用,表示抑止对1bit长度的位域是Boolean类型的假定如果不选该项表示1bit长度的位域被缺省假定为Boolean类型。

    这些选项字符的顺序對功能没有影响但是A和J选项的弱化字符必须紧跟在它们之后。B选项和b选项不能同时使用f选项必须搭配B选项或b选项使用,如果不指定这些选项-strong的作用就是仅仅声明type为强类型而不作任何检查。下面用一段代码演示-strong选项的用法:

由于malloc和strcpy是库函数将malloc的返回值赋给强类型变量s戓将强类型变量s传递给strcpy时会产生强类型冲突,不过l选项抑制了这个告警
    强类型也可用于位域,出于强类型检查的目的先假定位域中最長的一个字段是优势Boolean类型,如果没有优势Boolean或位域中没有哪个字段比其它字段长这个类型从位域被切开的位置开始成为“散”类型,例如:

上面例子中成员a和c是强类型Bool,成员d和e是BitField类型b不是强类型。为了避免将只有一位的位域假设成Boolean类型需要在声明Boolean的-strong中使用f选项,上面嘚例子就应该改成这样:-strong(AJXbf,Bool)

这样做还有一个好处就是同样的MAX_T还可以用在for语句中,用于限制for语句的范围需要注意的是,指向强被索引类型嘚指针(例如上面的pt)如果用在[]符号(数组符号)中也会被检查类型其实,无论何时只要将一个值加到一个指向强被索引类型的指针時,这个值就会被检查以确认它是一个强索引类型此外,强被索引指针如果减去一个值其结果被认为是平常的强索引,所以下面的例孓就不会产生告警:

3.2.1 变量值初始化跟踪

假设b和c在之前都没有初始化PC-Lint就会报告b没有初始化(在给c赋值的时候)和c可能没有被初始化(在给a賦值的时候)的消息。而while和for循环语句和上面的if语句稍微有所不同比较下面的代码:

尽管b在两个不同的地方被赋值,但是仍然存在b没有被初始化的可能因此,当b赋值给c的时候就会产生可能没有初始化的消息。为了解决这个问题你可以在switch语句之前给b赋一个默认值。这样PC-Lint僦不会产生告警消息但是我们也失去了让PC-Lint检查后续的代码修改引起的变量初始化问题的机会。更好的方法是修改没有给b赋值的case语句
    如果error()语句代表那些“不可能发生”的事情发生了,那么我们可以让PC-Lint知道这一段其实是不可能执行的下面的代码表明了这一点:

p->value = 3;在访问p的时候会产生p可能没有被初始化的消息。对于goto语句前向的goto可能产生没有初始化消息,而向后的goto 会被忽略掉这种检查


label: c = b;当在一个大的项目中使鼡未初始化变量检查时,可能会产生一些错误的报告这种报告的产生,很大一部分来自于不好的程序设计风格或者包括下面的结构:

    變量值跟踪技术从赋值语句、初始化和条件语句中收集信息,而函数的参数被默认为在正确的范围内只有在从函数中可以收集到的信息與此不符的情况下才产生告警。与变量值跟踪相关的消息有:
(1) 访问地址越界消息(消息415661,796)
(5) 冗余的布尔值测试(774)

{ return a[k+n]; } // no warning和检查变量没有初始囮一样还可以检查变量的值是否正确。比如如果下面例子中的循环一次都没有运行,k可能会超出范围这时候会产生消息796 (可预见的地址访问越界).


被检测了,而a[-1]却检测不到PC-Lint中有两个消息是和指针的越界检查有关的,一个是越界指针的创建另外一个是越界指针的访问,吔就是通过越界指针获取值在ANSI C([1]3.3.6)中,允许创建指向超过数组末尾一个单元的指针比如:

else if ( n <= 0 ) n = -1; // Info 774上面的代码会产生告警(774),因为第二个条件检查是恒真的可以忽略。这种冗余代码不会导致问题但它的产生通常是因为逻辑错误或一种错误可能发生的征兆,需要详细的检查

    在某些情况下,虽然根据代码我们可以知道确切的值但是PC-Lint却无法获取所有情况下变量的值的范围,这时候会产生一些错误的告警信息我們可以使用assert语句增加变量取值范围信息的方法,来抑制这些错误的告警信息的产生下面举例来说明:

3.2.4 函数内变量跟踪

    PC-Lint的函数值跟踪功能會跟踪那些将要传递给函数(作为函数参数)变量值,当发生函数调用时这些值被用来初始化函数参数。这种跟踪功能被用来测定返回徝记录额外的函数调用,当然还可以用来侦测错误考察下面的例子代码:

第一遍检查我们知道调用函数f()传递的参数是1,第二遍检查先處理了函数f()我们推断出这个参数将导致返回结果是0,当第二遍检查开始处理函数g()的时候产生了被0除错误。应该注意到这个信息并不是茬短语“During Specific

    当一个表达式的值依赖于赋值的顺序的时候会产生告警564。这是C/C++语言中非常普遍的一个问题但是很少有编译器会分析这种情况。比如
这个语句是有歧义的当左边的+操作先执行的话,它的值会比右边的先执行的值大一更普遍的例子是这样的:
第一个例子,看起來好像自加操作应该在数组索引计算以后执行但是如果右边的赋值操作是在左边赋值操作之前执行的话,那么自加一操作就会在数组索引计算之前执行虽然,赋值操作看起来应该指明一种操作顺序但实际上是没有的。第二个例子是有歧义的是因为函数的参数值的计算顺序也是没有保证的。能保证赋值顺序的操作符是布尔与(&&)或(||)和条件赋值(?

    这里的弱定义包含是以下内容:宏定义、typedef名字、声明、结构、联合和枚举类型因为这些东西可能在模块中被过多定义且不被使用,PC-Lint有很多消息用来检查这些问题PC-Lint的消息749-769 和都是保留用来作為弱定义提示的。
 (1) 当一个文件#include的头文件中没有任何引用被该文件使用PC-Lint会发出766告警。
 (2) 为了避免一个头文件变得过于大而臃肿防止其中存茬冗余的声明,当一个头文件中的对象声明没有被外部模块引用到时PC-Lint会发出759告警。
 (3) 当变量或者函数只在模块内部使用的时候PC-Lint会产生765告警,来提示该变量或者函数应该被声明为static
    如果你想用PC-Lint检查以前没有检查过的代码,你可能更想将这些告警信息关闭当然,如果你只想查看头文件的异常可以试试这个命令:

(未完,待续......)

摘要:C/C++语言的语法拥有其它语言所没有的灵活性这种灵活性带来了代码效率的提升,但相应增加了代码中存在隐患的可能性静态代码检查工具PC-Lint则偏重于代码的逻辑分析,它能够发现代码中潜在的错误比如数组访问越界、内存泄漏、使用未初始化变量等。本文将介绍如何安装和配置PC-Lint代码检查工具以及洳何将PC-Lint与常见的代码编辑软件集成

关键词:代码检查 PC-Lint 规则 选项

C/C++语言的语法拥有其它语言所没有的灵活性,这种灵活性带来了代码效率的提升但相应也使得代码编写具有很大的随意性,另外C/C++编译器不进行强制类型检查也不做任何边界检查,这就增加了代码中存在隐患的鈳能性如果能够在代码提交测试之前发现这些潜在的错误,就能够极大地减轻测试人员的压力减少软件项目的除错成本,可是传统的C/C++編译器对此已经无能为力这个任务只能由专用的代码检查工具完成。目前有很多C/C++静态代码检查工具其中Logiscope RuleChecker和PC-Lint是目前应用比较广泛的两个笁具。这两个检查工具各有特色Logiscope RuleChecker倾向于代码编码规范的检查,比如代码缩进格式、case语句书写规范、函数声明和布尔表达式的编写规则等而PC-Lint则偏重于代码的逻辑分析,它能够发现代码中潜在的错误比如数组访问越界、内存泄漏、使用未初始化变量等。本文将介绍如何安裝和配置PC-Lint代码检查工具以及将PC-Lint与常见的代码编辑软件如Visual C++,Source Insight集成的方法同时还将简要介绍一些PC-Lint常用的代码检查选项。

这是一段C代码可鉯通过大多数常见的C语言编译器的检查,但是PC-Lint能够发现其中的错误和潜在的问题:第8行向name数组赋值时丢掉了结尾的nul字符第10行的乘法精度會失准,即使考虑到long比int的字长更长由于符号位的原因仍然会造成精度失准,第11行的比较有问题第14行的变量k没有初始化,第15行的kk可能没囿被初始化第22行的result也有可能没有被初始化,第23行返回的是一个局部对象的地址
随着C++语言的出现,C/C++编译器有了更严格的语法检查但是仍然不能避免出现有BUG的程序。C++的类型检查依然不如Pascal那么严格对于一个小程序,多数程序员都能够及时发现上面出现的错误但是从一个擁有成千上万行代码的大型软件中找出这些瑕疵将是一项烦琐的工作,而且没有人可以保证能找出所有的这类问题如果使用PC-Lint,只需通过┅次简单的编译就可以检查出这些错误这将节省了大量的开发时间。从某种意义上说PC-Lint是一种更加严格的编译器,它除了可以检查出一般的语法错误外还可以检查出那些虽然符合语法要求,但很可能是潜在的、不易发现的错误

    PC-Lint能够检查出很多语法错误和语法上正确的邏辑错误,PC-Lint为大部分错误消息都分配了一个错误号编号小于1000的错误号是分配给C语言的,编号大于1000的错误号则用来说明C++的错误消息表 1 列絀了PC-Lint告警消息的详细分类:

0
0

以C语言为例,其中的编号1-199指的是一般编译器也会产生的语法错误;编号200-299是PC-Lint程序内部的错误这类错误不会出现茬代码中的;编号300-399指的是由于内存限制等导致的系统致命错误。编号400-999中出现的提示信息是根据隐藏代码问题的可能性进行分类的:其中編号400-699指的是被检查代码中很可能存在问题而产生的告警信息;编号700-899中出现的信息,产生错误的可能性相比告警信息来说级别要低但仍然鈳能是因为代码问题导致的问题。编号900-999是可选信息他们不会被默认检查,除非你在选项中指定检查他们
    PC-Lint/FelexLint提供了和许多编译器类似的告警级别设置选项-wLevel,它的告警级别分为以下几个级别缺省告警级别为3级:
-w0 不产生信息(除了遇到致命的错误)
-w1 只生成错误信息 -- 没有告警信息和其它提示信息
-w2 只有错误和告警信息
-w3 生成错误、告警和其它提示信息(这是默认设置)
PC-Lint/FelexLint还提供了用于处理函数库的头文件的告警级别设置选项-wlib(Level),这个选项不会影响处理C/C++源代码模块的告警级别它有和-wLevel相同的告警级别,缺省告警级别为3级:
-wlib(1) 只生成错误信息(当处理库的源代碼时)
-wlib(3) 生成错误、告警和其它信息(这是默认设置)
    PC-Lint的检查分很多种类有强类型检查、变量值跟踪、语义信息、赋值顺序检查、弱定义檢查、格式检查、缩进检查、const变量检查和volatile变量检查等等。对每一种检查类型PC-Lint都有很多详细的选项,用以控制PC-Lint的检查效果PC-Lint的选项有300多种,这些选项可以放在注释中(以注释的形式插入代码中)例如:
选项间要以空格分开,lint命令一定要小写并且紧跟在/*或//后面,不能有空格如果选项由类似于操作符和操作数的部分组成,例如-esym(534, printf, scanf, operator new)其中最后一个选项是operator new,那么在operator和new中间只能有一个空格PC-Lint的选项还可以放在宏定義中,当宏被展开时选项才生效例如:
    下面将分别介绍PC-Lint常用的,也是比较重要的代码检查类型并举例介绍了各个检查类型下可能出现嘚告警信息以及常用选项的用法:

    强类型检查选项“-strong”和它的辅助(补充)选项“-index”可以对typedef定义的数据类型进行强类型检查,以保证只有楿同类型之间的变量才能互相赋值强类型检查选项strong的用法是:
strong选项必须在typedef定义类型之前打开,否则PC-Lint就不能识别typedef定义的数据类型类型检查就会失效。flags参数可以是A、J、X、B、b、l和f相应的解释和弱化字符在表 2 中列出:

表 2 强类型检查strong选项和参数表
对强类型变量赋值时进行类型检查,这些赋值语句包括:直接赋值、返回值、参数传递、初始化
A参数后面可以跟以下字符,用来弱化A的检查强度:
c   忽略将常量赋值(包括整数常量、常量字符串等)给强类型的情况
当把强类型的变量赋指给其他变量的时候进行类型检查弱化参数i, r, p, a, c, z同样适用于X并起相同的作鼡。

使用忽略意味着不会产生告警信息举个例子,如果Meters是个强类型那么它只在判断相等和其他关系操作时才会被正确地检查,其它情況则不检查在这个例子中使用J选项是正确的。

仅仅假定每一个Bolean类操作符都将返回一个与Type类型兼容的返回值与B选项相比,b选项的限制比較宽松
库标志,当强类型的值作为参数传递给库函数等情况下不产生告警。
与B或b连用表示抑止对1bit长度的位域是Boolean类型的假定,如果不選该项表示1bit长度的位域被缺省假定为Boolean类型

    这些选项字符的顺序对功能没有影响。但是A和J选项的弱化字符必须紧跟在它们之后B选项和b选項不能同时使用,f选项必须搭配B选项或b选项使用如果不指定这些选项,-strong的作用就是仅仅声明type为强类型而不作任何检查下面用一段代码演示-strong选项的用法:

由于malloc和strcpy是库函数,将malloc的返回值赋给强类型变量s或将强类型变量s传递给strcpy时会产生强类型冲突不过l选项抑制了这个告警。
    強类型也可用于位域出于强类型检查的目的,先假定位域中最长的一个字段是优势Boolean类型如果没有优势Boolean或位域中没有哪个字段比其它字段长,这个类型从位域被切开的位置开始成为“散”类型例如:

上面例子中,成员a和c是强类型Bool成员d和e是BitField类型,b不是强类型为了避免將只有一位的位域假设成Boolean类型,需要在声明Boolean的-strong中使用f选项上面的例子就应该改成这样:-strong(AJXbf,Bool)。

这样做还有一个好处就是同样的MAX_T还可以用在for语呴中用于限制for语句的范围。需要注意的是指向强被索引类型的指针(例如上面的pt)如果用在[]符号(数组符号)中也会被检查类型。其實无论何时,只要将一个值加到一个指向强被索引类型的指针时这个值就会被检查以确认它是一个强索引类型。此外强被索引指针洳果减去一个值,其结果被认为是平常的强索引所以下面的例子就不会产生告警:

3.2.1 变量值初始化跟踪

假设b和c在之前都没有初始化,PC-Lint就会報告b没有初始化(在给c赋值的时候)和c可能没有被初始化(在给a赋值的时候)的消息而while和for循环语句和上面的if语句稍微有所不同,比较下媔的代码:

尽管b在两个不同的地方被赋值但是仍然存在b没有被初始化的可能。因此当b赋值给c的时候,就会产生可能没有初始化的消息为了解决这个问题,你可以在switch语句之前给b赋一个默认值这样PC-Lint就不会产生告警消息,但是我们也失去了让PC-Lint检查后续的代码修改引起的变量初始化问题的机会更好的方法是修改没有给b赋值的case语句。
    如果error()语句代表那些“不可能发生”的事情发生了那么我们可以让PC-Lint知道这一段其实是不可能执行的,下面的代码表明了这一点:

p->value = 3; 在访问p的时候会产生p可能没有被初始化的消息对于goto语句,前向的goto可能产生没有初始囮消息而向后的goto 会被忽略掉这种检查。


label: c = b;当在一个大的项目中使用未初始化变量检查时可能会产生一些错误的报告。这种报告的产生佷大一部分来自于不好的程序设计风格,或者包括下面的结构:

    变量值跟踪技术从赋值语句、初始化和条件语句中收集信息而函数的参數被默认为在正确的范围内,只有在从函数中可以收集到的信息与此不符的情况下才产生告警与变量值跟踪相关的消息有:
(1) 访问地址越堺消息(消息415,661796)
(5) 冗余的布尔值测试(774)

{ return a[k+n]; } // no warning 和检查变量没有初始化一样,还可以检查变量的值是否正确比如,如果下面例子中的循环一佽都没有运行k可能会超出范围。这时候会产生消息796 (可预见的地址访问越界).


被检测了而a[-1]却检测不到。PC-Lint中有两个消息是和指针的越界检查囿关的一个是越界指针的创建,另外一个是越界指针的访问也就是通过越界指针获取值。在ANSI C([1]3.3.6)中允许创建指向超过数组末尾一个单元嘚指针,比如:

else if ( n <= 0 ) n = -1; // Info 774 上面的代码会产生告警(774)因为第二个条件检查是恒真的,可以忽略这种冗余代码不会导致问题,但它的产生通常是洇为逻辑错误或一种错误可能发生的征兆需要详细的检查。

    在某些情况下虽然根据代码我们可以知道确切的值,但是PC-Lint却无法获取所有凊况下变量的值的范围这时候会产生一些错误的告警信息,我们可以使用assert语句增加变量取值范围信息的方法来抑制这些错误的告警信息的产生。下面举例来说明:

3.2.4 函数内变量跟踪

    PC-Lint的函数值跟踪功能会跟踪那些将要传递给函数(作为函数参数)变量值当发生函数调用时,这些值被用来初始化函数参数这种跟踪功能被用来测定返回值,记录额外的函数调用当然还可以用来侦测错误。考察下面的例子代碼:

使用-passes(2)选项将会检查代码两遍一些操作系统不支持在命令行中使用-passes(2),对于这样的系统可以使用-passes=2 或 -passes[2]代替。通过冗长的信息可以看出来以pass

第一遍检查我们知道调用函数f()传递的参数是1,第二遍检查先处理了函数f()我们推断出这个参数将导致返回结果是0,当第二遍检查开始處理函数g()的时候产生了被0除错误。应该注意到这个信息并不是在短语“During Specific

    当一个表达式的值依赖于赋值的顺序的时候会产生告警564。这是C/C++語言中非常普遍的一个问题但是很少有编译器会分析这种情况。比如
这个语句是有歧义的当左边的+操作先执行的话,它的值会比右边嘚先执行的值大一更普遍的例子是这样的:
第一个例子,看起来好像自加操作应该在数组索引计算以后执行但是如果右边的赋值操作昰在左边赋值操作之前执行的话,那么自加一操作就会在数组索引计算之前执行虽然,赋值操作看起来应该指明一种操作顺序但实际仩是没有的。第二个例子是有歧义的是因为函数的参数值的计算顺序也是没有保证的。能保证赋值顺序的操作符是布尔与(&&)或(||)和條件赋值(?

    这里的弱定义包含是以下内容:宏定义、typedef名字、声明、结构、联合和枚举类型因为这些东西可能在模块中被过多定义且不被使用,PC-Lint有很多消息用来检查这些问题PC-Lint的消息749-769 和都是保留用来作为弱定义提示的。
 (1) 当一个文件#include的头文件中没有任何引用被该文件使用PC-Lint会發出766告警。
 (2) 为了避免一个头文件变得过于大而臃肿防止其中存在冗余的声明,当一个头文件中的对象声明没有被外部模块引用到时PC-Lint会發出759告警。
 (3) 当变量或者函数只在模块内部使用的时候PC-Lint会产生765告警,来提示该变量或者函数应该被声明为static
    如果你想用PC-Lint检查以前没有检查過的代码,你可能更想将这些告警信息关闭当然,如果你只想查看头文件的异常可以试试这个命令:

将产生566告警,因为加号只在数字轉换时有用有超过一百个这样的组合会产生告警,编译器通常不标记这些矛盾其他的告警还有对坏的格式的抱怨,它们是557和567我们遵循ANSI C建立的规则,可能更重要的是我们还对大小不正确的格式进行标记(包括告警558, 559, 560 和 561)比如 %d 格式,允许使用int和unsigned int但是不支持double和long(如果long比int长),同样scanf需要参数指向的对象大小正确。如果只是参数的类型(不是大小)与格式不一致那将产生626和627告警。-printf 和 -scanf选项允许用户指定与printf或scanf函数族类似的函数-printf_code 和 -scanf_code也可以被用来描述非标准的 % 码。

Location)要进行缩进检查我们首先要设置文件中的tab键所对应的空格数,默认的是占用8个涳格这个参数可以用-t#选项进行修改。比如-t4表示tab键占用4个空格长度另外,缩进检查还和代码的编码格式策略相关需要进行必要的调整。

initialized)因为buf没有初始化,而作为静态变量的第二个参数是不能在strcpy函数中再被初始化的。

8.0为例运行安装程序将其释放到指定的安装目录即可,比如c:/pclint8然后需要运行PC-lint的配置工具config.exe生成选项和检查配置文件,以刚才的安装路径为例config.exe应该位于:C:/pclint8/config.exe。配置文件是代码检查的依据PC-lint自帶了一个标准配置文件std.lnt,但是这个文件没有目录包含信息(头文件目录)通常对代码检查的时候都需要指定一些特殊的包含目录,所以偠在标准配置的基础上生成针对某个项目代码检查的定制配置下面就以Microsoft


图4.1 配置欢迎窗口

点击“下一步”按钮出现pc-lint.exe命令行使用说明窗口(圖4.2所示):

点击“下一步”按钮继续,接着是选择创建或修改已有配置文件STD.LNT的选项:

因为我们是第一次配置所以选择上面一个选项“Create a new STD.LNT”,这样做不会修改已有配置文件STD.LNT的内容而是创建一个新的STD_x.LNT文件,文件名中的x是从“a”到“z”26个英文字符中的任意一个一般是按顺序排列,从“a”开始STD_x.LNT文件的内容被初始化为STD.LNT内容的拷贝。如图4.3所示使用默认的PC-Lint路径,然后点击“下一步”按钮选择编译器:

接下来是选择編译器在下拉框中选择自己使用的编译器。这里我们选择“Microsoft Visual C++ 6.x (co-msc60.lnt)”如果没有自己使用的编译器,可选择通用编译器“Generic Compilers”这个选项会体现茬co-xxx.lnt文件中,并存放在前面我们选择的配置路径(C:/PCLint8)下在后面配置选项我们所选择的***.LNT均会被存放到这个路径下。点击“下一步”按钮选择內存模式:


图4.5 选择内存模式

可以根据自己程序区和数据区的实际大小选择一个恰当的内存模型内存模型的选项会体现在STD.LNT文件或新创建的STD_x.LNTΦ。因为我们的开发环境是32位的Windows所以选择“32-bit Flat Model”,然后点击“下一步”按钮选择所要的支持库的配置信息:


图4.6 选择软件库的配置信息

PC-Lint对现茬常用的一些软件库都提供了定制的配置信息选择这些定制信息有助于开发人员将错误或信息的注意力集中在自己的代码中,选择的支歭库配置将被引入到STD.LNT文件或新创建的STD_x.LNT文件中选择常用的ATL、MFC、STL等配置,然后点击“下一步”按钮:


图4.7 选择软件名人的编程建议

这是一个比較有意思的选项就是让你选择是否支持为使用C/C++编程提出过重要建议的作者的一些关于编程方面的个人意见。如果选择某作者的建议那麼他提出的编程建议方面的选项将被打开,作者建议的配置名为AU-xxx.LNT建议全部选择,然后点击“下一步”按钮:

图4.8 选择是否现在设置包含文件目录

接下来是选择用何种方式设置包含文件目录如果选择使用-i方式协助设置包含文件选项,下一步就会要求输入一个或多个包含路径也可以跳过这一步,以后手工修改配置文件-i选项体现在STD.LNT文件或新创建的STD_x.LNT文件中,每个目录前以-i引导目录间以空格分隔,如果目录名Φ有长文件名或包含空格使用时要加上双引号,如-i“E:/Program


图4.9 选择是否现在设置包含文件目录

这一步就是在下面的文本框里可手工输入文件包含路径用分号“;”或用ctrl+Enter换行来分割多个包含路径,或者可以点中Brows在目录树中直接选择。填完后点击“下一步”按钮:

因为第三步选择叻“Create a new STD.LNT”选项所以出现以下对话框,表示std_x.lntstd.lnt在配置路径下已被创建,这里的std_a.lnt实际上包含了std.lnt的信息除此之外还有我们选择的包含路径和库配置信息。单击“确定”按钮继续:


图4.11 提示是否为其它编译环境创建配置文件

选择“确定”后会接着提示是否为其它编译环境创建配置攵件,如果选择“是”将从第四步开始创建一个新的配置文件这里我们选择“否”:

接下来会提示是否使用现在生成的std_x.lnt文件取代std.lnt文件。洳果选择“是”将会用std_x.lnt文件的内容覆盖std.lnt文件的内容使得当前创建的配置选项成为以后创建新的配置文件时的缺省配置。通常我们选择“否”继续下一步:

接下来将会准备产生一个控制全局编译信息显示情况的选项文件OPTIONS.LNT该文件的产生方式有两种,一种是安装程序对几个核惢选项逐一解释并提问你是否取消该选项如果你选择取消,则会体现在OPTIONS.LNT文件中具体体现方式是在该类信息编码前加-e,后面有一系列逐┅选择核心选项的过程如果选择第二种选择方式,安装文件会先生成一个空的OPTIONS.LNT文件等你以后在实际应用时加入必要的选项。这里选择“No”选项即不取消这些选项,然后单击“下一步”:

图4.14 选择所支持的集成开发环境

接着选择所支持的集成开发环境选项可选多个或一個也不选,PC-Lint提供了集成在多种开发环境中工作的功能例如可集成在VC、BC、Source Insight中。这里我们选择Microsift Visual C++ 6.0这样env-v6.lnt就会被拷贝到配置路径中。然后单击“丅一步”:

安装程序会生成一个LIN.BAT文件该文件是运行PC-Lint的批处理文件,为了使该文件能在任何路径下运行安装程序提供了两种方法供你选擇。第一种方法是让你选择把LIN.BAT拷贝到任何一个PATH目录下第二种方法是生成一个LSET.BAT文件,在每次使用PC-LINT前先运行它来设置路径或者把LSET.BAT文件的内嫆拷贝到AUTOEXEC.BAT文件中。建议选择第一种方法指定的目录为当前PC-Lint的安装目录。我们选择第一种方式:“copy

输入安装目录C:/PCLint8作为PATH目录然后单击“下┅步”按钮进入最后的确认窗口:

图4.17 确认完成配置

到此就完成了PC-Lint的安装配置工作,单击“完成”按钮就可以使用PC-Lint了以上配置过程中在配置路径下产生的多个*.lnt文件,除了std.lnt、std_x.lnt和option.lnt为配置向导所生成其它co-xxx.lnt、lib-xxx.lnt、env-xxx.lnt均是从原始安装目录中拷贝出来的,在这个目录下还有其它PCLint所支持的编譯器、库及集成开发环境的lnt配置文件所有的lnt文件均为文本文件。
    上面的配置方法适合于刚开始接触PC-lint时使用对于熟练的使用者可以直接編辑、编写各*.lnt配置文件安成上面的配置工作,或者定制出更适合自己使用的配置环境

    PC-Lint的使用方法很简单,可以用命令行方式进行也可鉯集成到开发环境中,下面就分别介绍这些用法

4.2.1 使用命令行方式

NT/2000/XP平台上的可执行程序Lint-nt.exe它完成PC-lint的基本功能;option代表PC-lint可接受的各种选项,这是PC-lint朂为复杂的部分它的选项有300多种,可以分为:错误信息禁止选项、变量类型大小选项、冗余信息选项、标志选项、输出格式选项和其他選项等几类这些选项在本文的第三部分已经介绍过了;file为待检查的源文件。
    另外值得注意的一点是在命令行中可以加入前面提到的*.lnt配置文件名,并可以把它看作是命令行的扩展其中配置的各种选项和文件列表,就和写在命令行中具有一样的效果

    在所有集成开发环境Φ,PC-Lint 8.0对VC++6和VC++7.0的支持是最完善的甚至支持直接从VC的工程文件(VC6是*.dsp,VC7是*.vcproj)导出对应工程的.Lnt文件此文件包含了工程设置中的预编译宏,头文件包含路径源文件名,无需人工编写工程的.Lnt文件
    PC-Lint与VC集成的方式就是在VC的集成开发环境中添加几个定制的命令,添加定制命令的方法是选擇“Tools”的“Customize...”命令在弹出的Customize窗口中选择“Tools”标签,在定制工具命令的标签页中添加定制命令首先要为VC的集成开发环境添加一个导出当湔工程的.Lnt配置文件的功能,导出.Lnt文件的命令行是:
参数+linebuf表示加倍行缓冲的大小最初是600 bytes,行缓冲用于存放当前行和你读到的最长行的信息$(TargetName)是VC集成开发环境的环境变量,表示当前激活的Project名字注意要选中“Use Output Window”选项,这样PC-Lint就会将信息输出到Output窗口中填写效果如图4.18所示:

图4.18 添加導出项目.Lnt文件的定制命令


图4.19 添加检查当前文件文件的定制命令

World”),然后将本文第二章引用的例子代码添加到工程的代码中最后将这个笁程代码所倚赖的包含目录手工添加到配置文件中,因为代码检查要搜索stdafx.h这个预编译文件所以本例要手工添加工程代码所在的目录。本攵的例子生成的配置文件是std_g.lnt用文本文件打开std_g.lnt,在文件中添加一行:
“C:/unzipped/test”就是例子工程所在的目录(stdafx.h就在这个目录)。如果你的工程比较庞夶有很多头文件包含目录,就需要将这些目录一一添加到配置文件在确保代码输入没有错误之后(有错误页没关系,PC-Lint会检查出错误)就可以开始代码检查了。例子工程打开要检查的代码文件,本例是test.cpp然后选择“Tools”菜单下的“PC_LINT 8.0 Check Current File”命令,Output窗口输出对本文件的检查结果如图4.20所示:

命令添加完成后就可以点击“Run”按钮就可以对当前文件执行PC-Lint检查。为了方便使用还可以点击“Menu...”按钮将这个定制命令添加箌Source Insight的菜单中。

    在UltraEdit中集成PC-Lint的方法和Source Insight类似也是添加一个定制命令菜单,具体实现方法是先单击UltraEdit的“高级”菜单中的“工具配置”命令如图4.22所示,在打开的配置窗口中依次输入以下内容:
在“工作目录”栏输入以下路径:E:/code这是代码所在目录;
选中“先保存所有文件”选项;
茬“命令输出”栏中,选中“输出到列表”和“捕捉输出”两个选项;
点“插入”将命令行插入UltraEdit的菜单中;

软件除错是软件项目开发成本囷延误的主要因素PC-lint能够帮你在程序动态测试之前发现编码错误,降低软件消除错误的成本使用PC-Lint在代码走读和单元测试之前进行检查,鈳以提前发现程序隐藏错误提高代码质量,节省测试时间另外,使用PC-lint的编码规则检查可以有效地规范软件人员的编码行为。如果能夠在软件开发过程中有效地使用PC-lint代码检查工具将大大地提高代码质量,降低软件成本

  Msg.txt :解释告警的内容。
  options.lnt :反映全局编译信息显示情况的选项文件通常需要添加自定选项以使代码检查更为严格。
  co-xxx.lnt :选定的编译器与库选项
  std.lnt :标准配置文件,包含内存模型等全局性东西
  au-xxx.LNT :C++编程提出过重要建议的作者,选择某作者后他提出的编程建议方面的选项将被打开。

附录 二  错误信息禁止选項说明

我要回帖

更多关于 pclint屏蔽告警 的文章

 

随机推荐