Sun Studio 12:C 用户指南

第 4 章 lint 源代码检验器

本章介绍如何使用 lint 程序检查 C 代码中可能导致编译失败或运行时出现意外结果的错误。在很多情况下,lint 会警告您存在编译器未对其作必要标志的不正确、有错误倾向或非标准的代码。

lint 程序会发出 C 编译器生成的每条错误消息和警告消息。它还发出关于潜在错误和可移植性问题的警告。由 lint 发出的许多消息有助于您提高程序的效率,其中包括减小其大小和减少必需的内存。

lint 程序使用与编译器相同的语言环境,并且lint 的输出会定向到 stderr。有关在执行基于类型的别名歧义消除之前如何使用 lint 检查代码的更多信息和示例,请参见4.6.3 lint 过滤器

4.1 基本和增强 lint 模式

lint 程序在以下两种模式下运行:

在基本模式和增强模式下,lint 通过标记文件(包括已使用的任何库)间定义和用法中的不一致,来补偿 C 中的单独编译和独立编译。特别是在大型项目环境中,同一函数可能被不同程序员用在数百个单独的代码模块中,在这种情况下,lint 有助于发现借助其他方式很难发现的错误。例如,如果调用函数时使用的参数比所需的参数少一个,该函数在栈中查找该调用从未推的值,结果在一个条件下正确,在另一个条件下不正确,具体取决于内存中该栈位置发生的情况。通过标识类似的依赖性以及对计算机体系结构的依赖性,lint 可提高运行于您的计算机或其他计算机上的代码的可靠性。

在增强模式下,lint 提供比在基本模式下更详细的报告。在基本模式下,lint 的功能包括:

在增强模式下,lint 可以检测以下问题:

4.2 使用 lint

可从命令行调用 lint 程序及其选项。要在基本模式下调用 lint,请使用以下命令:


% lint file1.c file2.c

可使用 -Nlevel-Ncheck 选项调用增强 lint。例如,可以按如下所示调用增强 lint


% lint -Nlevel=3 file1.c file2.c

lint 会检查两遍代码。第一遍,lint 检查 C 源文件中的错误条件 ;第二遍,检查 C 源文件中的不一致性。除非使用 -c 调用 lint,否则对于用户该过程不可见:


% lint -c file1.c file2.c

该命令指示 lint 仅执行第一遍检查,并在名为 file1.lnfile2.ln 的中间文件中收集 file1.cfile2.c 间定义和用法中的不一致信息(这些信息和第二遍检查相关):


% ls
file1.c
file1.ln
file2.c
file2.ln

可见,lint 的选项 -c 类似于 cc 的选项 -c,可禁止编译的链接编辑阶段。一般说来,lint 的命令行语法严格遵循 cc 的语法。

针对 .ln 文件执行 lint 时:


% lint file1.ln file2.ln

将执行第二遍检查。lint 按文件在命令行中出现的顺序处理任意多个 .c.ln 文件。因此,


% lint file1.ln file2.ln file3.c

指示 lint 检查 file3 .c 中的内部错误以及所有三个文件中的不一致性。

lint 按与 cc 相同的顺序在目录中搜索包含的头文件。可以像使用 cc 的选项 -I 那样使用 lint 的选项 -I。请参见2.14 如何指定 include 文件

可以在同一命令行中指定 lint 的多个选项。除非其中一个选项带有参数或者选项有多个字母,否则选项可以并置:


% lint -cp -Idir1 -Idir2 file1.c file2.c

该命令指示 lint 执行以下操作:

lint 有许多选项,可用来指示 lint 执行某些任务并报告某些条件。

4.3 lint 选项

lint 程序是一个静态分析器。它不能求出它检测到的依赖性的运行时结果。例如,某些程序可能包含数百个执行不到的 break 语句,这些语句并不重要,但是 lint 仍然会标记它们。下面是一个示例,其中包含 lint 命令行选项和指令(嵌入源代码文本的特殊注释):

下面按字母顺序列出了 lint 选项,其中有几个 lint 选项与禁止 lint 诊断消息有关。在这些按字母顺序说明的选项之后有一个表 4–8,其中也列出了这些选项,并且列出了这些选项禁止的特定消息。用于调用增强 lint 的选项以 -N 开头。

lint 能够识别许多 cc 命令行选项,其中包括 -A-D-E-g-H-O-P-U-Xa-Xc-Xs-Xt-Y,虽然 -g-O 会被忽略。未识别的选项被警告并忽略。

4.3.1 -#

打开详细模式,显示调用的每个组件。

4.3.2 -###

显示调用但实际上并不执行的每个组件。

4.3.3 -a

禁止某些消息。请参阅表 4–8

4.3.4 -b

禁止某些消息。请参阅表 4–8

4.3.5 -C filename

使用指定的文件名创建一个 .ln 文件。这些 .ln 文件仅是 lint 的第一遍检查产生的文件。filename 可以是完整路径名。

4.3.6 -c

为命令行上命名的每个 .c 文件创建一个 .ln 文件,该文件包含与 lint 的第二遍检查相关的信息。不执行第二遍检查。

4.3.7 -dirout=dir

指定目录 dir,其中将存放 lint 输出文件(.ln 文件)。此选项会影响 -c 选项。

4.3.8 -err=warn

-err=warn-errwarn=%all 的宏。请参见4.3.15 -errwarn=t

4.3.9 -errchk=l(, l)

执行l 指定的附加检查。缺省值为 -errchk=%none。指定 -errchk 与指定 -errchk=%all 等效。l 是一个以逗号分隔的检查列表,由下表中的一个或多个值组成。例如,-errchk=longptr64,structarg

表 4–1 -errchk 标志

值 

含义 

%all

执行 -errchk 的所有检查。

%none

不执行 -errchk 的任何检查。这是缺省值。

[no%]locfmtchk

lint 的第一遍检查期间检查类似 printf 的格式字符串。无论是否使用 -errchk=locfmtchklint 都会在第二遍检查期间检查类似 printf 的格式字符串。

[no%]longptr64

检查是否可移植到其长整型和指针大小为 64 位、平整型大小为 32 位的环境。即使使用了显式强制类型转换,也检查指针表达式和长整型表达式是否赋值为平整型。 

[no%]structarg

检查通过值传递的结构参数,并在形式参数类型未知时报告情况。 

[no%]parentheses

检查代码中优先级的透明度。使用此选项可增强代码的可维护性。如果 -errchk=parentheses 返回一个警告,请考虑使用额外的括号明确地表示代码中操作的优先级。

[no%]signext

检查如下情况:标准 ISO C 值保留规则允许在无符号整型的表达式中进行带符号整型值的符号扩展。仅当同时指定 -errchk=longptr64 时,该选项才会产生错误消息。

[no%]sizematch

检查较长整数到较短整数的赋值并发出警告。对于具有不同符号的相同长度的整数之间的赋值(无符号整型数获取带符号整型数),也会发出这些警告。 

4.3.10 -errfmt=f

指定 lint 输出的格式。f 可以是下列值之一:macrosimplesrctab

表 4–2 -errfmt 标志

值 

含义 

macro

显示错误的源代码、行号和位置,并展开宏。 

simple

对于一行(简单)诊断消息,在括号中显示错误的行号和位置号。类似于 -s 选项,但是包含错误的位置信息。

src

显示错误的源代码、行号和位置(不展开宏)。 

tab

以制表格式显示。这是缺省值。 

缺省值为 -errfmt=tab。指定 -errfmt 与指定 -errfmt=tab 等效。

如果指定了多种格式,则使用最后指定的格式,并且 lint 发出有关未使用格式的警告。

4.3.11 -errhdr=h

在同时指定了 -Ncheck 的情况下,允许 lint 报告头文件的某些消息。h 是一个以逗号分隔的列表,它包含以下项中的一项或多项:dirno%dir%all%none%user

表 4–3 -errhdr 标志

值 

含义 

dir

报告目录 dir 中包含的头文件的 -Ncheck 消息。

no%dir

不报告目录 dir 中包含的头文件的 -Ncheck 消息。

%all

检查使用的所有头文件。 

%none

不检查头文件。这是缺省值。 

%user

检查所有已使用的用户头文件,即,除 /usr/include 及其子目录中的头文件以及由编译器提供的头文件之外的所有头文件。

缺省值为 -errhdr=%none。指定 -errhdr 与指定 -errhdr=%user 等效。

示例:


% lint -errhdr=inc1 -errhdr=../inc2

检查目录 inc1../inc2 中已使用的头文件。


% lint -errhdr=%all,no%../inc

检查除目录 ../inc 中的头文件之外的所有已使用的头文件。

4.3.12 -erroff=tag(, tag)

禁止或启用 lint 错误消息。

t 是一个逗号分隔列表,它包含以下项中的一项或多项:tagno%tag%all%none

表 4–4 -erroff 标志

值 

含义 

tag

禁止由该 tag 指定的消息。可通过 -errtags=yes 选项来显示消息的标记。

no%tag

启用由该 tag 指定的消息。

%all

禁止所有消息。 

%none

启用所有消息。这是缺省值。 

缺省值为 -erroff=%none。指定 -erroff 与指定 -erroff=%all 等效。

示例:


% lint -erroff=%all,no%E_ENUM_NEVER_DEF,no%E_STATIC_UNUSED

仅打印消息 "enum never defined" 和 "static unused",并禁止其他消息。


% lint -erroff=E_ENUM_NEVER_DEF,E_STATIC_UNUSED

仅禁止消息 "enum never defined" 和 "static unused"。

4.3.13 -errsecurity=v

可使用 -errsecurity 选项检查代码中的安全漏洞。

v 必须是下列值之一:

表 4–5 -errsecurity 标志

值 

含义 

core 

此级别检查的源代码构造几乎始终是不安全或难以验证的。此级别的检查包括: 

  • 将可变格式字符串与 printf()scanf() 系列函数一起使用

  • scanf() 函数中使用无限制字符串 (%s) 格式

  • 使用无安全用法的函数:gets()cftime()ascftime()creat()

  • 错误地使用 open()O_CREAT

    将在此级别生成警告的源代码视为错误。应更改有问题的源代码。在所有情况下,都应采用更安全简单的代码。

standard 

此级别检查包括 core 级别的所有检查,以及可能安全、但有更好的可用替代代码的构造的检查。检查新近编写的代码时建议采用此级别检查。此级别的其他检查包括: 

  • 使用除 strlcpy() 之外的字符串复制函数

  • 使用弱随机数函数

  • 使用不安全的函数生成临时文件

  • 使用 fopen() 创建文件

  • 使用调用 shell 的函数

    使用新的或经过大幅修改的代码替换在此级别生成警告的源代码。应权衡消除传统代码中的这些警告对应用程序造成的不稳定风险。

extended 

此级别检查包含几乎所有检查,包括 Core 级别和 Standard 级别的所有检查。此外,还会生成许多有关在某些情况下可能不安全的构造的警告。此级别的检查可用作检查代码的辅助措施,但无需将这些检查用作判断源代码是否可接受的标准检查。此级别的其他检查包括: 

  • 在循环中调用 getc()fgetc()

  • 使用易于产生路径名争用情况的函数

  • 使用 exec() 函数系列

  • stat() 和其他函数之间的争用情况

    检查在此级别生成警告的源代码,确定是否存在潜在安全问题。

%none 

关闭 -errsecurity 检查

如果未指定 -errsecurity 的设置,lint 会将它设置为 -errsecurity=%none。如果在指定 -errsecurity 时未指定参数,lint 会将它设置为 -errsecurity=standard

4.3.14 -errtags=a

显示每条错误消息的消息标记。a 可以是 yesno。缺省值为 -errtags=no。指定 -errtags 与指定 -errtags=yes 等效。

可与所有 -errfmt 选项一起使用。

4.3.15 -errwarn=t

如果发出了指定警告消息,lint 会以失败状态退出。t 是一个以逗号分隔的列表,它包含以下项中的一项或多项:tagno%tag%all%none。这些值的顺序很重要;例如,如果发出了除 tag 以外的任何警告,%all,no%tag 会导致 lint 以致命状态退出。下表列出了 -errwarn 值:

表 4–6 -errwarn 标志

tag

如果此 tag 指定的消息作为警告消息发出,将导致 lint 以致命状态退出。如果未出现 tag,则没有影响。

no%tag

如果 tag 指定的消息仅作为警告消息发出,将防止 lint 以致命状态退出。如果未发出 tag,则没有影响。使用此选项,可防止发出先前使用该选项及 tag%all 指定的警告消息时导致 lint 以致命状态退出。

%all

如果发出了任何警告消息,将导致 lint 以致命状态退出。%all 可以后跟 no%tag,以避免该行为的特定警告消息。

%none

如果发出了任何警告消息,将防止任何警告消息导致 lint 以致命状态退出。

缺省值为 -errwarn=%none。如果单独指定 -errwarn,它与 -errwarn=%all 等效。

4.3.16 -F

引用命令行上命名的 .c 文件时,会打印命令行上提供的路径名而不仅仅是它们的基名。

4.3.17 -fd

报告有关旧样式函数的定义或声明情况。

4.3.18 -flagsrc=file

使用文件 file 中包含的选项执行 lint。可在 file 中指定多个选项(每行一个)。

4.3.19 -h

禁止某些消息。请参阅表 4–8

4.3.20 -Idir

目录 dir 中搜索包含的头文件。

4.3.21 -k

改变 /* LINTED [message] */ 指令或 NOTE(LINTED(message)) 注释的行为。通常,lint 会针对这些指令后面的代码禁止警告消息。如果不设置该选项,lint 不禁止消息,而是打印包含指令或注释内部的注释的附加消息。

4.3.22 -Ldir

-l 一起使用时,在目录 dir 中搜索 lint 库。

4.3.23 -lx

访问 lintllib-lx.ln

4.3.24 -m

禁止某些消息。请参阅表 4–8

4.3.25 -m32|-m64

指定所分析程序的内存模型。还搜索与所选的内存模型(32 位/64 位)相对应的 lint 库。

使用 -m32 可验证 32 位 C 程序;使用 -m64 可验证 64 位程序。

在所有 Solaris 平台和不支持 64 位的 Linux 平台上,ILP32 内存模型(32 位 int、long、pointer 数据类型)是缺省值。在支持 64 位的 Linux 平台上,LP64 内存模型(64 位 long 和 pointer 数据类型)是缺省值。-m64 仅允许在支持 LP64 模型的平台上使用。

请注意,在以前的编译器发行版中,内存模型 ILP32 或 LP64 是通过选择 -Xarch 选项来隐式指定的。从 Sun Studio 12 编译器开始,就不再是这样了。在大多数平台上,只需在命令行上添加 -m64,就可以通过 lint 调用 64 位程序。

有关预定义的宏的更多信息,请参见以下 lint 选项列表后面的几节内容。

4.3.26 -Ncheck=c

检查头文件中的相应声明;检查宏。c 是一个以逗号分隔的检查列表,它包含以下项中的一项或多项:macroextern%all%noneno%macrono%extern

表 4–7 -Ncheck 标志

值 

含义 

macro

检查文件之间的宏定义的一致性。 

extern

检查源文件与关联的头文件(例如,file1.cfile1.h)之间声明的一一对应关系。确保头文件中既无多余的 extern 声明,也不缺少 extern 声明。

%all

执行 -Ncheck 的所有检查。

%none

不执行 -Ncheck 的任何检查。这是缺省值。

no%macro

不执行 -Ncheck 的任何 macro 检查。

no%extern

不执行 -Ncheck 的任何 extern 检查。

缺省值为 -Ncheck=%none。指定 -Ncheck 与指定 -Ncheck=%all 等效。

多个值可以用逗号分隔,例如 -Ncheck=extern,macro

示例:


% lint -Ncheck=%all,no%macro

执行除宏检查之外的所有检查。

4.3.27 -Nlevel=n

通过指定用于报告问题的增强 lint 分析级别,可打开增强 lint 模式。此选项允许您控制检测到的错误量。级别越高,检验时间越长。n 是一个数字:1234。没有缺省值。如果未指定 -Nlevellint 会使用其基本分析模式。如果指定不带任何参数的 -Nlevellint 会设置 -Nlevel=4

有关基本和增强 lint 模式的说明,请参见4.2 使用 lint

4.3.27.1 -Nlevel=1

分析单个过程。报告发生在某些程序执行路径上的无条件错误。不执行全局数据和控制流分析。

4.3.27.2 -Nlevel=2

分析整个程序,包括全局数据和控制流。报告发生在某些程序执行路径上的无条件错误。

4.3.27.3 -Nlevel=3

分析整个程序,包括常量传播、常量用作实际参数时的情况以及在 -Nlevel=2 下执行的分析。

在此分析级别检验 C 程序所用的时间比前一级别长 2 到 4 倍。需要额外的时间是因为 lint 通过为程序变量创建可能值的集合对程序进行部分解释。这些变量集合是以常量以及包含程序中的常量操作数的条件语句为基础创建的。这些集合形成创建其他集合的基础(一种常量传播形式)。作为分析结果接收的集合根据以下算法来评估正确性:

如果对象的所有可能值之中存在一个正确值,则该正确值用作进一步传播的基础;否则诊断出一个错误。

4.3.27.4 -Nlevel=4

分析整个程序,并报告使用某些程序执行路径时会发生的条件错误,以及在 -Nlevel=3 下执行的分析。

在此分析级别上,存在更多诊断消息。分析算法通常对应于 -Nlevel=3 的分析算法,所不同的是任何无效值现在会生成一条错误消息。在此级别上进行分析所需的时间增大两个数量级(大约慢 20 到 100 倍)。在这种情况下,所需的额外时间与以递归、条件语句等为特征的程序复杂性成正比。因此,对超过 100,000 行的程序使用此级别的分析可能很困难。

4.3.28 -n

禁止检查与缺省 lint 标准 C 库的兼容性。

4.3.29 -ox

导致 lint 创建一个名为 llib-lx.lnlint 库。该库是从 lint 在其第二遍检查中使用的所有 .ln 文件创建的。-c 选项会使 -o 选项的任何使用无效。要生成 llib-lx.ln 而不产生任何无关消息,可以使用 -x 选项。如果 lint 库的源文件仅为外部接口,则 -v 选项会很有用。如果使用 -lx 调用 lint,则生成的 lint 库可以在以后使用。

缺省情况下,使用 lint 的基本格式创建库。如果使用 lint 的增强模式,则创建的库将为增强格式,并且只能在增强模式下使用。

4.3.30 -p

启用某些与可移植性问题相关的消息。

4.3.31 -Rfile

.ln 文件写入 file,以供 cxref(1) 使用。如果此选项打开,此选项将禁用增强模式。

4.3.32 -s

生成具有 "warning:" 或 "error:" 前缀的简单诊断。缺省情况下,lint 会缓冲某些消息以生成复合输出。

4.3.33 -u

禁止某些消息。请参阅表 4–8。此选项适用于对较大程序的文件子集运行 lint

4.3.34 -V

将产品名称及发行版写入标准错误输出。

4.3.35 -v

禁止某些消息。请参阅表 4–8

4.3.36 -Wfile

.ln 文件写入 file,以供 cflow(1) 使用。如果此选项打开,此选项将禁用增强模式。

4.3.37 -XCC=a

接受 C++ 样式的注释。特别是,可使用 // 指示注释的开始。a 可以是 yesno。缺省值为 -XCC=no。指定 -XCC 与指定 -XCC=yes 等效。


注 –

仅当使用 -xc99=none 时,才需使用此选项。使用 -xc99=all(缺省值)时,lint 接受由 // 指示的注释。


4.3.38 -Xalias_level[=l ]

其中,l下列值之一:anybasicweaklayoutstrictstdstrong。有关不同的歧义消除级别的详细说明,请参见表 B–11

如果未指定 -Xalias_level,该标志的缺省值为 -Xalias_level=any。这意味着不存在基于类型的别名分析。如果指定了 -Xalias_level,但未提供级别,则缺省值为 -Xalias_level=layout

请确保运行 lint 时所在的歧义消除级别不如运行编译器时所在的级别严格。如果运行 lint 时所在的歧义消除级别比编译时所在的级别更严格,结果将很难解释并且可能令人误解。

有关歧义消除的详细说明以及有助于歧义消除的 pragma 的列表,请参见4.6.3 lint 过滤器

4.3.39 -Xarch=amd64

(Solaris 操作系统)已过时。不要使用。请参见4.3.25 -m32|-m64

4.3.40 -Xarch=v9

(Solaris 操作系统)已过时。不要使用。请参见4.3.25 -m32|-m64

4.3.41 -Xc99[= o]

-Xc99 标志控制编译器对 C99 标准(ISO/IEC 9899:1999,编程语言-C)中已实现功能的识别。

o 可以是下列值之一:allnone

-Xc99=none 会关闭对 C99 功能的识别。-Xc99=all 会打开对支持的 C99 功能的识别。

指定不带任何参数的 -Xc99 与指定 -Xc99=all 等效。


注 –

虽然编译器的支持级别缺省为表 C–6 中列出的 C99 功能,但是 Solaris 软件在 /usr/include 中提供的标准头文件仍不符合 1999 ISO/IEC C 标准。如果遇到错误消息,请尝试使用 -Xc99=none 获取这些头文件的 1990 ISO/IEC C 标准行为。


4.3.42 -Xexplicitpar=a

(SPARC) 指示 lint 识别 #pragma MP 指令。a 可以是 yesno。缺省值为 -Xexplicitpar=no。指定 -Xexplicitpar 与指定 -Xexplicitpar=yes 等效。

4.3.43 -Xkeeptmp=a

保留执行 lint 期间创建的临时文件,而非自动删除它们。a 可以是 yesno。缺省值为 -Xkeeptmp=no。指定 -Xkeeptmp 与指定 -Xkeeptmp=yes 等效。

4.3.44 -Xtemp=dir

将临时文件的目录设置为 dir。如果没有此选项,临时文件将放在 /tmp 中。

4.3.45 -Xtime=a

报告执行每遍 lint 检查所需的时间。a 可以是 yesno。缺省值为 -Xtime=no。指定 -Xtime 与指定 -Xtime=yes 等效。

4.3.46 -Xtransition=a

针对 K&R C 和 Sun ISO C 之间的差异发出警告。a 可以是 yesno。缺省值为 -Xtransition=no。指定 -Xtransition 与指定 -Xtransition=yes 等效。

4.3.47 -Xustr={ascii_utf16_ushort| no}

选项允许编译器将 U"ASCII_string" 形式的串文字识别成无符号短整型的数组。缺省值为 -Xustr=no,它禁止编译器识别 U"ASCII_string" 串文字。-Xustr=ascii_utf16_ushort 允许编译器识别 U"ASCII_string" 串文字。

4.3.48 -x

禁止 某些消息。请参阅表 4–8

4.3.49 -y

命令行上命名的各个 .c 文件视为以指令 /* LINTLIBRARY */ 或注释 NOTE(LINTLIBRARY) 开头。lint 库通常是使用 /* LINTLIBRARY */ 指令或 NOTE(LINTLIBRARY) 注释创建的。

4.4 lint 消息

lint 的大多数消息都很简单,对于它们诊断的每个问题都打印一行语句。对于包含的文件中检测到的错误,编译器会报告多次,而 lint 仅报告一次(无论其他源文件中包含该文件多少次)。文件之间存在不一致时会发出复合消息,并且在少数情况下,文件内部存在问题时也会发出复合消息。单个消息描述所检查的文件中的各个问题。如果使用 lint 过滤器(请参见4.6.2 lint)要求对出现的每个问题都打印一条消息,可通过调用带有 -s 选项的 lint 将复合诊断转换为简单类型。

lint 的消息会写入 stderr

4.4.1 用于禁止消息的选项

可以使用几个 lint 选项禁止 lint 诊断消息。可以使用后跟一个或多个 tag-erroff 选项禁止消息。可以使用 -errtags=yes 选项显示这些助记符标记。

下表列出了用于禁止 lint 消息的选项。

表 4–8 用于禁止消息的 lint 选项

选项 

禁止的消息 

-a

assignment causes implicit narrowing conversion

conversion to larger integral type may sign-extend incorrectly

-b

statement not reached (unreachable break and empty statements)

-h

assignment operator "=" found where equality operator "==" was expected

constant operand to op: "!"

fallthrough on case statements

pointer cast may result in improper alignment

precedence confusion possible; parenthesize

statement has no consequent: if

statement has no consequent: else

-m

declared global, could be static

-erroff=tag

tag 指定的一条或多条 lint 消息

-u

name defined but never used

name used but not defined

-v

arguments unused in function

-x

name declared but never used or defined

4.4.2 lint 消息格式

通过某些选项,lint 程序可以显示精确的源文件行以及指向发生错误的行所在位置的指针。启用此功能的选项是 -errfmt=f。如果设置了此选项,lint 会提供以下信息:

例如,以下程序 Test1.c 包含一个错误。


1 #include <string.h>
2 static void cpv(char *s, char* v, unsigned n)
3 { int i;
4   for (i=0; i<=n; i++){
5        *v++ = *s++;}
6 }
7 void main(int argc, char* argv[])
8 {
9     if (argc != 0){
10        cpv(argv[0], argc, strlen(argv[0]));}
11}

如果针对 Test1.c 使用带有以下选项的 lint


% lint -errfmt=src -Nlevel=2 Test1.c

产生以下输出:


      |static void cpv(char *s, char* v, unsigned n)
      |            ^  line 2, Test1.c
      |
      |         cpv(argv[0], argc, strlen(argv[0]));
      |                      ^  line 10, Test1.c
warning: improper pointer/integer combination: arg #2
      |
      |static void cpv(char *s, char* v, unsigned n)
      |                               ^  line 2, Test1.c
      |
      |cpv(argv[0], argc, strlen(argv[0]));
      |                       ^ line 10, Test1.c
      |
      |        *v++ = *s++;
      |         ^  line 5, Test1.c
warning:use of a pointer produced in a questionable way
    v defined at Test1.c(2)    ::Test1.c(5)
      call stack:
          main()                ,    Test1.c(10)
          cpv()                 ,    Test1.c(5)

第一个警告指示互相矛盾的两个源代码行。第二个警告显示调用栈以及导致错误的控制流。

另一个程序 Test2.c 包含一个不同的错误:


1 #define AA(b) AR[b+l]
2 #define B(c,d) c+AA(d)
3
4 int x=0;
5
6 int AR[10]={1,2,3,4,5,6,77,88,99,0};
7
8 main()
9  {
10  int y=-5, z=5;
11  return B(y,z);
12 }

如果针对 Test2.c 使用带有以下选项的 lint


% lint -errfmt=macro Test2.c

产生以下输出,并显示宏替换的步骤:


      | return B(y,z);
      |        ^  line 11, Test2.c
      |
      |#define B(c,d) c+AA(d)
      |                 ^  line 2, Test2.c
      |
      |#define AA(b) AR[b+l]
      |                   ^  line 1, Test2.c
error: undefined symbol: l
|
      |    return B(y,z);
      |           ^  line 11, Test2.c
      |
      |#define B(c,d) c+AA(d)
      |                 ^  line 2, Test2.c
      |
      |#define AA(b) AR[b+l]
      |                   ^  line 1, Test2.c
variable may be used before set: l
lint: errors in Test2.c; no output created
lint: pass2 not run - errors in Test2.c

4.5 lint 指令

4.5.1 预定义值

以下预定义在所有模式下均有效:

以下预定义在 -Xc 模式下无效:

4.5.2 指令

现有注释支持 /*...*/ 形式的 lint 指令,但将来的注释不支持这些指令。建议对所有注释使用源代码注释形式 (NOTE(...)) 的指令。

通过包括 note.h 文件可指定源代码注释形式的 lint 指令,例如:

#include <note.h>

Lint 与其他多种工具共享源代码注释方案。安装 Sun C 编译器时,还会自动安装 /usr/lib/note/SUNW_SPRO-lint 文件,该文件包含 LockLint 可识别的所有注释的名称。然而,Sun C 源代码检验器 lint 也会检查 /usr/lib/note/opt/SUNWspro/prod/lib/note 下的所有文件以查找所有有效的注释。

可通过设置环境变量 NOTEPATH 来指定除 /usr/lib/note 以外的位置,如下所示:


setenv NOTEPATH $NOTEPATH:other_location

下表列出了 lint 指令及其操作。

表 4–9 lint 指令

指令 

操作 

NOTE(ALIGNMENT(fname,n )) 其中,n=1、2、4、8、16、32、64、128

指示 lint 将其后面的函数结果设置为以 n 字节对齐。例如,将 malloc() 定义为实际返回字(甚至双字)对齐的指针时返回一个 char* 或 void*。

禁止以下消息: 

  • improper alignment

NOTE(ARGSUSED(n))

/*ARGSUSEDn*/

该指令的作用类似于下一个函数的 -v 选项。

禁止针对其后的函数定义中除前 n 个参数之外的各个参数发出以下消息。缺省值为 0。对于 NOTE 格式,必须指定 n

  • argument unused in function

NOTE(ARGUNUSED

(par_name[,par_name...] ))

指示 lint 不检查提到的参数的用法(此选项仅适用于下一个函数)。

禁止对 NOTE 或指令中列出的各个参数发出以下消息。

  • argument unused in function

NOTE(CONSTCOND)

/*CONSTCOND*/

禁止发出关于条件表达式的常量操作数的警告消息。禁止对该指令之后的构造发出以下消息。亦作 NOTE(CONSTANTCONDITION)

/* CONSTANTCONDITION */

constant in conditional context

constant operands to op: "!"

logical expression always false: op "&&"

logical expression always true: op "||"

NOTE(EMPTY) /*EMPTY*/

禁止由于 if 语句而引起 null 语句时发出警告信息。该指令应放在测试表达式之后和分号之前。当空 if 语句后跟有效 else 语句时,提供该指令以支持空 if 语句。它禁止针对空 else 结论发出消息。

禁止在 if 的控制表达式与分号之间插入时发出以下消息。

  • statement has no consequent: else

    else 与分号之间插入时;

  • statement has no consequent: if

NOTE(FALLTHRU)

/*FALLTHRU*/

禁止在 casedefault 带标签语句失败时发出警告消息。该指令应放在标签之前并紧挨着标签。

禁止对该指令之后的 case 语句发出以下消息。亦作 NOTE(FALLTHROUGH)/* FALLTHROUGH */

  • fallthrough on case statement

NOTE(LINTED (msg))

/*LINTED [msg]*/

禁止所有文件内警告(处理未使用的变量或函数时发出的警告除外)。该指令应放在紧挨着发生 lint 警告的位置之前的行上。-k 选项改变 lint 处理该指令的方式。 lint 会打印注释中包含的附加消息(如果有),而非禁止消息。该指令可与 -s 选项一起用于 post-lint 过滤。

未调用 -k 时,禁止对该指令之后的代码行发出关于文件内问题的各个警告,但以下警告除外:

  • argument unused in function

  • declarations unused in block

  • set but not used in function

  • static unused

  • variable not used in function

    忽略 msg

NOTE(LINTLIBRARY)

/*LINTLIBRARY*/

调用 -o 时,仅将该指令之后的 .c 文件中的定义写入库 .ln 文件。该指令禁止发出关于此文件中存在未使用的函数和函数参数的警告消息。

NOTE(NOTREACHED)

/*NOTREACHED*/

在适当的点,停止关于无法执行到的代码的注释。此注释通常放在诸如 exit(2) 的函数调用之后。

禁止在函数末尾该指令之后存在右花括号时发出以下消息。 

  • statement not reached

    对于该指令之后的无法执行到的语句;

  • fallthrough on case statement

    对于该指令之后无法从前面的 case 执行到的 case

  • function falls off bottom without returning value

NOTE(PRINTFLIKE(n))

NOTE(PRINTFLIKE(fun_name,n) )

/*PRINTFLIKEn*/

将其后函数定义中的第 n 个参数视为 [fs]printf() 格式字符串,并在其余参数与转换定义之间不匹配时发出以下消息。缺省情况下,如果标准 C 库提供的 [fs]printf() 函数调用中存在错误,lint 会发出这些警告。

对于 NOTE 格式,必须指定 n

  • malformed format strings

    对于该参数中的无效转换定义,以及函数参数类型与格式不一致;

  • too few arguments for format

  • too many arguments for format

NOTE(PROTOLIB(n))

/*PROTOLIBn*/

n 为 1,并且使用了 NOTE(LINTLIBRARY)/* LINTLIBRARY */ 时,仅将该指令之后的 .c 文件中的函数原型声明写入库 .ln 文件。缺省值为 0,它取消该进程。

对于 NOTE 格式,必须指定 n

NOTE(SCANFLIKE(n))

NOTE(SCANLIKE(fun_name,n ))

/*SCANFLIKEn*/

NOTE(PRINTFLIKE(n))/* PRINTFLIKEn */ 相同,只是函数定义的第 n 个参数会被视为 [fs]scanf() 格式字符串。缺省情况下,如果标准 C 库提供的 [fs]scanf() 函数调用中存在错误,lint 会发出警告。

对于 NOTE 格式,必须指定 n

NOTE(VARARGS(n))

NOTE(VARARGS(fun_name,n ))

/*VARARGSn*/

禁止对以下函数声明中可变数量的参数执行常规检查。检查前 n 个参数的数据类型;缺少 n 时,会将其视为 0。对于新代码或更新后代码,建议在其定义中使用省略号 (...) 终结符。

对于其定义在该指令之后的函数,禁止在调用带有 n 个或更多参数的函数时发出以下消息。对于 NOTE 格式,必须指定 n

  • 使用可变数量的参数调用的函数

4.6 lint 参考和示例

本节提供有关 lint 的参考信息,包括由 lint 执行的检查、lint 库以及 lint 过滤器。

4.6.1 由 lint 执行的诊断

对以下三种主要条件执行特定于 lint 的诊断:不一致的使用、不可移植的代码以及可疑的构造。在本节中,我们将研究在每种条件下 lint 的行为示例,并针对它们引起的问题提供可能的解决方法建议。

4.6.1.1 一致性检查

在文件内部以及各文件之间检查使用变量、参数和函数的不一致性。一般说来,对原型的使用、声明和参数执行的检查lint 对旧样式函数执行的检查相同。如果程序未使用函数原型,lint 将比编译器更严格地检查每个函数调用中参数的数量和类型。lint 还标识 [fs]printf()[fs]scanf() 控制字符串中转换定义和参数之间的不匹配。

示例:

4.6.1.2 可移植性检查

lint 的缺省行为会标记某些不可移植的代码,如果使用 -p-Xc 调用 lint 还会诊断其他一些情况。后者导致 lint 检查不符合 ISO C 标准的构造。有关使用 -p-Xc 时发出的消息,请参见4.6.2 lint

示例:


char c;
c = getchar();
if (c == EOF) ...

其中 EOF 的值为 -1,在字符变量取非负值的计算机上总是失败。使用 -p 调用的 lint 会检查暗示无格式 char 可取负值的所有比较。然而,在以上示例中,将 c 声明为 signed char 可避免执行诊断,却无法避免出现该问题。这是因为 getchar() 必须返回所有可能的字符和一个不同的 EOF 值,因此 char 无法存储其值。此示例可能是出自于实现定义的符号扩展的最常见示例,我们之所以引用该示例,是为了说明如何通过巧妙地应用 lint 的可移植性选项来帮助您发现与可移植性无关的错误。在任何情况下,将 c 声明为 int


short s;
long l;
s = l;

lint 在缺省情况下会标记所有此类赋值;可通过调用 -a 选项来禁止该诊断。请记住,使用此选项或任何其他选项调用 lint 时,可能会禁止其他诊断。请查看4.6.2 lint中的列表,以了解禁止多项诊断的选项。


int *fun(y)
char *y;
{
    return(int *)y;
}

这是因为在大多数计算机上,int 不能在一个任意字节边界上开始,而 char 则可以。可通过使用 -h 调用 lint 来禁止诊断,尽管这可能会再次禁用其他消息。但是最好通过使用通用指针 void * 来消除该问题。


int a[10];
main()
{
    int i = 1;
    a[i++] = i;
}

在此示例中,a[1] 的值在使用一个编译器时可能为 1,在使用另一个编译器时可能为 2。如果在逻辑运算符 && 的位置错误地使用了按位逻辑运算符 &,则会引起该诊断:


if ((c = getchar()) != EOF & c != ’0’)

4.6.1.3 可疑的构造

lint 会标记那些不能表达编程人员意图的各种合法构造。示例:


unsigned x;
if (x < 0) ...

总是失败。测试:


unsigned x;
if (x > 0) ...

等效于:


if (x != 0) ...

这可能不是预期的操作。lint 会标记 unsigned 变量与负常量或 0 的可疑比较。要将 unsigned 变量与负数的位模式进行比较,请将该变量的类型强制转换为 unsigned


if (u == (unsigned) -1) ...

或者使用 U 后缀:


if (u == -1U) ...

int fun()
{
    int a, b, x, y;
    (a = x) && (b == y);
}

if (x & a == 0) ...

求值如下:


if (x & (a == 0)) ...

这很有可能不是您的意图。如果使用 -h 调用 lint,则会禁用该诊断

4.6.2 lint

可以使用 lint 库检查程序是否与已在其中进行调用的库函数兼容,其中包括函数返回类型的声明以及函数所预期参数的数量和类型等。标准 lint 库与 C 编译系统提供的库对应,并且通常存储在系统上的标准位置。按照约定,lint 库的名称采用 llib-lx.ln 形式。

lint 标准 C 库 lliblc.ln 在缺省情况下附加至 lint 命令行;可通过调用 -n 选项来禁止其兼容性检查。其他 lint 库作为 -l 的参数进行访问。即:


% lint -lx file1.c file2.c

指示 lint 检查 file1.cfile2.c 中函数和变量的用法是否与 lintllib-lx.ln 兼容。仅由定义组成的库文件完全作为普通源文件和普通 .ln 文件处理,区别在于不会针对以下情况发出错误消息:函数和变量在库文件中的用法不一致,或者函数和变量在库文件中已定义但未在源文件中使用。

要创建您自己的 lint 库,请在 C 源文件的开头插入指令 NOTE(LINTLIBRARY),然后使用 -o 选项以及指定给 -l 的库名称针对该文件调用 lint


% lint -ox file1.c file2.c

导致仅将源文件中以 NOTE(LINTLIBRARY) 开头的定义写入文件 llib-lx.ln。(请注意,lint -occ -o 类似。)可以相同的方式从函数原型声明的文件创建库,不同的是必须在声明文件的最前面插入 NOTE(LINTLIBRARY)NOTE(PROTOLIB(n))。如果 n 为 1,则原型声明写入库 .ln 文件,这与旧样式的定义相同。如果 n 为 0(缺省 值),则取消该进程。使用 -y 调用 lint 是创建 lint 库的另一种方法。命令行:


% lint -y -ox file1.c file2.c

导致该行中命名的每个源文件被视为以 NOTE(LINTLIBRARY) 开头,并且只有其定义被写入 llib-lx.ln

缺省情况下,lint 在标准位置搜索 lint 库。要指示 lint 在非标准位置的目录中搜索 lint 库,请使用 -L 选项指定目录路径:


% lint -Ldir -lx file1.c file2.c

在增强模式下,lint 生成 .ln 文件,这些文件存储的信息比在基本模式下生成的 .ln 文件存储的信息多。在增强模式下,lint 可以读取和理解由基本或增强 lint 模式生成的所有 .ln 文件。在基本模式下,lint 只能读取和理解由基本 lint 模式生成的 .ln 文件。

缺省情况下,lint 使用 /usr/lib 目录中的库。这些库采用基本 lint 格式。可以运行一次 makefile,并以新格式创建增强 lint 库,从而使增强 lint 更有效地工作。要运行 makefile 并创建新库,请输入以下命令:


% cd /opt/SUNWspro/prod/src/lintlib; make

其中 /opt/SUNWspro/prod 为安装目录。运行 makefile 之后,lint 在增强模式下使用新库,而不是使用 /usr/lib 目录中的库。

系统会在搜索标准位置之前先搜索指定的目录

4.6.3 lint 过滤器

lint 过滤器是特定于项目的后处理程序,通常使用 awk 脚本或类似程序读取 lint 的输出,并放弃项目认为没有标识字符串函数真正问题的消息(例如,返回值有时或总是被忽略)。 当 lint 选项和指令未提供对输出的足够控制时,lint 过滤器会生成定制的诊断报告。

lint 的两个选项在开发过滤器的过程中特别有用: