.NH What lint checks .NH 2 Set, used and unused variables .PP We make a distinction between two classes of variables: the class of automatic variables (including register variables) and the other variables. The other variables, global variables, static variables, formal parameters et cetera, are assumed to have a defined value. Global variables e.g., are initialized by the compiled code at zeros; formal parameters have a value which is equal to the value of the corresponding actual parameter. These variables can be used without explicitly initializing them. The initial value of automatic variables is undefined (if they are not initialized at declaration). These variables should be set before they are used. A variable is set by .IP .RS .IP 1. an assignment (including an initialization) .IP 2. taking the address .RE .PP The first case is clear. The second case is plausible. It would take to much effort (if at all possible) to check if a variable is set through one of its aliases. Because .I lint should not warn about correct constructs, it does this conservative approach. Structures (and unions) can also be set by setting at least one member. Again a conservative approach. An array can be set by using its name (e.g. as actual parameter of a function call). .I Lint warns for usage as .I rvalue of automatic variables which are not set. .PP A variable is used if .IP .RS .IP 1. it is used as a .I rvalue .IP 2 its address is taken .IP Arrays and structures (and unions) are also used if one entry or one member respectively is used. .RE .PP When a variable is never used in the part of the program where it is visible, a warning is given. For variables declared at the beginning of a compound statement, a check is made at the end of this statement. For formal parameters a check is made at the end of the function definition. At the end of a file this is done for global static definitions. For external variables a warning can be given when all the files are parsed. .NH 2 Flow of control .PP The way .I lint keeps track of the flow of control is best explained by means of an example. See the program of figure 1. .KF .DS B .ft CW if (cond) /* a statement which is executed if cond is true, * the if-part */ else /* the else-part */ .DE .br .ce .I figure\ 1. .R .KE .PP After evaluation of \f(CWcond\fP, two things can happen. The if-part is executed or the else-part is executed (but not both). Variables which are set in the if-part but not in the else-part, need not be set after the if statement, and vice versa. .I Lint detects this and assumes these variables after the if statement to be \fImaybe set\fR. (See figure 2.) .KF .DS B .ft CW int cond; main() { int i, j; if (cond) { i = 0; j = 0; } else use(i); /* i may be used before set */ use(j); /* maybe j used before set */ } .DE .br .ce .I figure 2. .R .KE .PP If both the if-part and the else-part are never left (i.e. they contain an endless loop or a return statement), .I lint knows that the if statement is never left too. Besides the if statement, .I lint knows the possible flows of control in while, do, for and switch statements. It also detects some endless loops like \f(CWwhile(1)\fP, \f(CWdo ... while (1)\fP, \f(CWfor (;;)\fP. .NH 2 Functions .PP Most C compilers will not complain if a function is called with actual parameters of a different type than the function expects. Using a function in one file as a function of type .I A while defining it in another file as a function of type .I B is also allowed by most compilers. It needs no explanation that this can lead to serious trouble. .PP .I Lint checks if functions are called with the correct number of arguments, if the types of the actual parameters correspond with the types of the formal parameters and if function values are used in a way consistently with their declaration. When the result of a function is used, a check is made to see if the function returns a value. When a function returns a value, .I lint checks if the values of all calls of this function are used. .NH 2 Undefined evaluation order .PP The semantics of C do not define evaluation orders for some constructs, which, at first sight, seem well defined. The evaluation order of the expression .ft CW a[i]\ =\ i++; .R e.g., is undefined. It can be translated to something with the semantics of .ft CW a[i]\ =\ i; i++; .R which is what probably was meant, or .ft CW a[i+1]\ =\ i; i++;. .R An easier example to explain why, is .ft CW j\ =\ a[i]\ +\ i++;. .R `\f(CW+\fR' Is a so called .I commutative operator (with respect to the evaluation order) , as is `\f(CW=\fR'. This allows the compiler to choose which term to evaluate first. It is easy to see, that it makes a difference for the value of .ft CW j, .R which order is chosen. The expression .ft CW i++ .R is said to have .I side effects. .R It affects the value of .ft CW i. .R Because this value is used in the other term, this gives a conflict. .PP A function call with reference to a variable as argument can have side effects to. Therefor, the evaluation order of .ft CW i .R in the expression .ft CW f(&i)\ +\ i .R is undefined. When a function is called with an array as argument, this array can be affected by the function, because only the address of the array is passed to the function. (In Pascal a copy of the array is passed to the function if the formal parameter is not declared \fIvar\fP.) So the evaluation order of .ft CW a .R in the expression .ft CW f(a)\ +\ a[0] .R is undefined. This one is not yet detected by .I lint. .PP Global variables can still cause trouble. If function .ft CW f .R affects the global variable .ft CW i, .R the value of the expression .ft CW f()\ +\ i .R is undefined, because the evaluation order of \f(CWi\fP is undefined. .PP The evaluation order of the arguments of a function is not defined, so the expression .ft CW f(i,\ i++) .R gives a warning .ft CW i evaluation order undefined. .R .NH 2 Pointer alignment problems .PP For pointers to objects of different types there are different alignment restrictions. On some machines pointers to type char can have both odd and even values, whereas pointers to type int should contain an even address. .I Lint could warn for all pointer conversions. This is not what .I lint does. .I Lint assumes that some pointers are more restricted than others, and that pointers of some types can safely be converted to a pointer of a less restrictive type. The order of restriction is as follows (`\(<=' means `is not more restricted than') : .PP .ce char \(<= short \(<= int \(<= long .ce float \(<= double .NH 2 Libraries .PP C is a small language. As a matter of fact it has no i/o routines. To make it a useful language, C is supported by libraries. These libraries contain functions and variables that can be used by any C program. .I Lint knows some libraries too. At this moment it knows the `-\fIlc\fR', `-\fIlm\fR' and `-\fIlcurses\fR' libraries. The `-\fIlc\fR' library, containing definitions for functions from chapter two and three of the \s-2UNIX\s+2 programmers manual, is default. .I Lint warns for definitions of functions or global variables with the same name as a function definition in a library. .bp