Oracle® Solaris Studio 12.4:C 用户指南

退出打印视图

更新时间: 2014 年 12 月
 
 

4.6.1 由 lint 执行的诊断

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

4.6.1.1 一致性检查

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

示例:

  • 在文件内部,lint 标记将返回但未向调用函数提供值的非 void 函数。过去,程序员通常通过省略返回类型指明某个函数不应返回值: fun() {}。该约定对于编译器无意义,编译器假定 fun() 具有返回类型 int。可使用返回类型 void 来声明函数以消除该问题。

  • 在文件之间,lint 检测非 void 函数不返回值但好像它在某个表达式中使用了值以及相反的情况(即,函数返回值,但在随后调用中有时或总是被忽略)。如果值始终被忽略,则可能存在函数定义低效,而值有时忽略,可能是编程风格不好(通常,不会测试错误条件)。如果无需检查 strcat()strcpy()sprintf() 等字符串函数或 printf()putchar() 等输出函数的返回值,可将违例调用强制转换为 void 类型。

  • lint 标识已声明但未使用或定义、已使用但未定义或已定义但未使用的变量或函数。将 lint 应用于某个集合中要一起装入的某些文件而非全部文件时,它会发出关于以下情况中的函数和变量的错误消息:

    • 在那些文件中声明,但在其他地方定义或使用

    • 在那些文件中使用,但在其他地方定义

    • 在那些文件中定义,但在其他地方使用

    可调用 -x 选项以禁止第一种情况,调用 -u 以禁止后两种情况。

4.6.1.2 可移植性检查

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

示例:

  • 在某些 C 语言实现中,未显式声明为 signedunsigned 的字符变量会被视为范围通常在 -128 到 127 之间的带符号值。在其他实现中,它们会被视为范围通常在 0 到 255 之间的非负值。以下测试(其中 EOF 的值为 -1)在字符变量接受非负值的机器上将始终失败。

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

    调用带 -plint 将检查隐含 plain char 可能具有负值的所有比较。然而,在示例中将 c 声明为signed char 可避免执行诊断,却无法避免出现该问题。getchar() 必须返回所有可能的字符和一个不同的 EOF 值,因此 char 无法存储其值。此示例可能是出自于实现定义的符号扩展的最常见示例,说明如何通过巧妙地应用 lint 的可移植性选项来帮助您发现与可移植性无关的错误。在任何情况下,将 c 声明为 int

  • 位字段也会出现类似的问题。将常量值赋给位字段时,该字段可能太小,无法容纳该值。在将 int 类型的位字段视为无符号值的计算机上,int x:3 所允许的值在 0 到 7 的范围内;而在将其视为带符号值的计算机上,相应值的范围在 -4 到 3 之间。但是,声明为类型 int 的三位字段无法在后一种计算机上存储值 4。使用 -p 调用的 lint 会标记除 unsigned intsigned int 之外的所有位字段类型。它们仅是可移植的位字段类型。编译器支持 intcharshortlong 位字段类型,它们可以为 unsignedsigned无格式。编译器还支持 enum 位字段类型。

  • 在将较长的类型赋值给较短的类型时,会出现问题。如果有效位被截断,则失去准确性:

    short s;
    long l;
    s = l;

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

  • 将指向某对象类型的指针强制转换为指向具有更严格对齐要求的对象类型的指针,可能将无法移植。lint 会标记以下示例,原因是在大多数计算机上,int 无法在任意字节边界启动,而 char 可以。

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

    可通过使用 -h 调用 lint 来禁止诊断,尽管这可能会再次禁用其他消息。但是最好通过使用通用指针 void * 来消除该问题。

  • ISO C 未定义复杂表达式的求值顺序。也就是说,如果由于某个表达式的求值而更改某个变量时,函数调用、嵌套赋值语句或加减运算符会引起副作用,那么副作用发生的顺序在极大程度上取决于计算机。在缺省情况下,lint 会标记由于副作用而更改并且在同一表达式的其他地方使用的任何变量:

    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 变量总是具有非负值。因此以下测试将始终失败:

    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) ...
  • lint 会标记用于预期出现副作用的上下文但没有副作用的表达式,即可能未表达程序员意图的表达式。它在应该出现赋值运算符的位置发现等号运算符(即预期出现副作用)时发出一个额外的警告:

    int fun()
    {
        int a, b, x, y;
        (a = x) && (b == y);
    }
  • lint 会提醒您注意给混合有逻辑运算符和按位运算符(特别是 &|^<<>>)的表达式加上括号,否则运算符优先级的误解会引起不正确的结果。例如,按位运算符 & 的优先级低于逻辑运算符 ==,所以表达式:

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

    求值如下:

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

    此结果很有可能不是所期望的。使用 -h 调用 lint 会禁用该诊断。