Go to main content
Oracle® Developer Studio 12.6: C User's Guide

Exit Print View

Updated: July 2017
 
 

5.6 lint Reference and Examples

This section provides reference information on lint, including checks performed by lint, lint libraries, and lint filters.

5.6.1 Diagnostics Performed by lint

lint-specific diagnostics are issued for three broad categories of conditions: inconsistent use, nonportable code, and questionable constructs. This section reviews examples of lint’s behavior in each of these areas, and suggests possible responses to the issues they raise.

5.6.1.1 Consistency Checks

Inconsistent use of variables, arguments, and functions is checked within files as well as across them. Generally speaking, the same checks are performed for prototype uses, declarations, and parameters as lint checks for old-style functions. If your program does not use function prototypes, lint checks the number and types of parameters in each call to a function more strictly than the compiler. lint also identifies mismatches of conversion specifications and arguments in [fs]printf() and [fs]scanf() control strings.

Examples:

  • Within files, lint flags non-void functions that return without giving a value to the invoking function. In the past, programmers often indicated that a function was not meant to return a value by omitting the return type: fun() {}. That convention has no meaning to the compiler, which assumes fun() has the return type int. Declare the function with the return type void to eliminate the problem.

  • Across files, lint detects cases where a non-void function does not return a value but is used in an expression as if it did, and the opposite problem where a function returns a value that is sometimes or always ignored. If the value is always ignored, an inefficiency in the function definition might be present, while sometimes ignoring the value could be bad programming style (typically, not testing for error conditions). If you do not need to check the return values of string functions like strcat(), strcpy(), and sprintf(), or output functions like printf() and putchar(), cast the offending calls to void.

  • lint identifies variables or functions that are declared but not used or defined, used, but not defined, or defined, but not used. When lint is applied to some but not all files of a collection to be loaded together, it issues error messages about functions and variables that are in the following situations:

    • Declared in those files but defined or used elsewhere

    • Used in those files but defined elsewhere

    • Defined in those files but used elsewhere

    Invoke the -x option to suppress the first situation, and -u to suppress the latter two.

5.6.1.2 Portability Checks

Some nonportable code is flagged by lint in its default behavior, and a few more cases are diagnosed when lint is invoked with -p or -pedantic. The latter causes lint to check for constructs that do not conform to the ISO C standard. For the messages issued under -p and -pedantic, see lint Libraries.

Examples:

  • In some C language implementations, character variables that are not explicitly declared signed or unsigned are treated as signed quantities with a range typically from -128 to 127. In other implementations, they are treated as non-negative quantities with a range typically from 0 to 255. The following test, where EOF has the value -1, always fails on machines where character variables take on non-negative values.

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

    lint invoked with -p checks all comparisons that imply a plain char may have a negative value. However, declaring c as a signed char in the example eliminates the diagnostic, not the problem. getchar() must return all possible characters and a distinct EOF value, so a char cannot store its value. This example, perhaps the most common one arising from implementation-defined sign-extension, shows how a thoughtful application of lint’s portability option can help you discover bugs not related to portability. In any case, declare c as an int.

  • A similar issue arises with bit-fields . When constant values are assigned to bit-fields, the field may be too small to hold the value. On a machine that treats bit-fields of type int as unsigned quantities, the values allowed for int x:3 range from 0 to 7, whereas on machines that treat them as signed quantities, they range from -4 to 3. However, a three-bit field declared type int cannot hold the value 4 on the latter machines. lint invoked with -p flags all bit-field types other than unsigned int or signed int. These are the only portable bit-field types. The compiler supports int, char, short, and long bit-field types that may be unsigned, signed, or plain. It also supports the enum bit-field type.

  • Problems can arise when a larger-sized type is assigned to a smaller-sized type. If significant bits are truncated, accuracy is lost:

    short s;
    long l;
    s = l;

    lint flags all such assignments by default; the diagnostic can be suppressed by invoking the –-a option. Note that you might be suppressing other diagnostics when you invoke lint with this or any other option. For the list of options that suppress more than one diagnostic, see the list in lint Libraries.

  • A cast of a pointer to one object type to a pointer to an object type with stricter alignment requirements might not be portable. lint flags the following example because, on most machines, an int cannot start on an arbitrary byte boundary, whereas a char can.

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

    You can suppress the diagnostic by invoking lint with -h, although, you might be again disabling other messages. You can eliminate the problem by using the generic pointer void *.

  • ISO C leaves the order of evaluation of complicated expressions undefined. That is, when function calls, nested assignment statements, or the increment and decrement operators cause side effects when a variable is changed as a by-product of the evaluation of an expression, the order in which the side effects take place is highly machine-dependent. By default, lint flags any variable changed by a side effect and used elsewhere in the same expression:

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

    In this example, the value of a[1] could be 1 with one compiler and 2 with a different compiler. The bitwise logical operator & can give rise to this diagnostic when it is mistakenly used in place of the logical operator &&:

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

5.6.1.3 Questionable Constructs

lint flags a miscellany of legal constructs that might not represent what the programmer intended. Examples:

  • An unsigned variable always has a non-negative value. So the following test always fails:

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

    The following test:

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

    is equivalent to:

    if (x != 0) ...

    This result might not be the intended action. lint flags questionable comparisons of unsigned variables with negative constants or 0. To compare an unsigned variable to the bit pattern of a negative number, cast it to unsigned:

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

    Or use the U suffix:

    if (u == -1U) ...
  • lint flags expressions without side effects that are used in a context where side effects are expected, that is, where the expression might not represent what the programmer intends. It issues an additional warning whenever the equality operator is found where the assignment operator is expected, that is, where a side effect is expected:

    int fun()
    {
        int a, b, x, y;
        (a = x) && (b == y);
    }
  • lint cautions you to use parenthesis for expressions that mix both the logical and bitwise operators (specifically, &, |, ^, <<, >>), where misunderstanding of operator precedence may lead to incorrect results. For example, because the precedence of bitwise & falls below logical ==, the expression:

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

    is evaluated as:

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

    This result is most likely not what was intended. Invoking lint with -h disables the diagnostic.

5.6.2 lint Libraries

You can use lint libraries to check your program for compatibility with the library functions you have called in it: the declaration of the function return type, the number and types of arguments the function expects, and so on. The standard lint libraries correspond to libraries supplied by the C compilation system, and generally are stored in a standard place on your system. By convention, lint libraries have names of the form llib-lx.ln.

The lint standard C library, llib-lc.ln, is appended to the lint command line by default. Checks for compatibility with it can be suppressed by invoking the -n option. Other lint libraries are accessed as arguments to -l. The following example directs lint to check the usage of functions and variables in file1.c and file2.c for compatibility with the lint library llib-lx.ln.

% lint -lx file1.c file2.c

The library file, which consists only of definitions, is processed exactly as the ordinary source files and ordinary .ln files, except that the functions and variables are used inconsistently in the library file, or defined in the library file but not used in the source files, elicit no complaints.

To create your own lint library, insert the directive NOTE(LINTLIBRARY) at the head of a C source file, then invoke lint for that file with the -o option and the library name given to -l. The following example causes only definitions in the source files headed by NOTE(LINTLIBRARY) to be written to the file llib-lx.ln.

% lint -ox file1.c file2.c

Note the analogy of lint -o to cc -o. A library can be created from a file of function prototype declarations in the same way, except that both NOTE(LINTLIBRARY) and NOTE(PROTOLIB(n))must be inserted at the head of the declarations file. If n is 1, prototype declarations are written to a library .ln file just as the old-style definitions. If n is 0, the default, the process is cancelled. Invoking lint with -y is another way of creating a lint library. The following command line causes each source file named on that line to be treated as if it begins with NOTE(LINTLIBRARY), and only its definitions to be written to llib-lx.ln.

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

By default, lint searches for lint libraries in the standard place. To direct lint to search for a lint library in a directory other than the standard place, specify the path of the directory with the -L option:

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

5.6.3 lint Filters

A lint filter is a project-specific post-processor that typically uses an awk script or similar program to read the output of lint and discard messages that your project has deemed as not identifying real problems, for example, string functions return values that are sometimes or always ignored. lint filters generate customized diagnostic reports when lint options and directives do not provide sufficient control over output.

Two options to lint are particularly useful in developing a filter:

  • The -s option causes compound diagnostics to be converted into simple, one-line messages issued for each occurrence of the problem diagnosed. The easily parsed message format is suitable for analysis by an awk script.

  • The -k option causes certain comments you have written in the source file to be printed in output. It can be useful both in documenting project decisions and specifying the post-processor’s behavior. In the latter instance, if the comment identifies an expected lint message and the reported message is the same, the message can be filtered out. To use -k, insert the NOTE(LINTED(msg))directive on the line preceding the code you want to comment, where msg refers to the comment to be printed when lint is invoked with -k.

    For more information about lint, when -k is not invoked for a file containing NOTE(LINTED(msg)). , see Table 15.