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 -Xc. The latter causes lint to check for constructs that do not conform to the ISO C standard. For the messages issued under -p and -Xc, see 4.6.2 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 nonnegative quantities with a range typically from 0 to 255. So the test:
char c; c = getchar(); if (c == EOF) ... |
where EOF has the value -1, always fails on machines where character variables take on nonnegative values. 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 above example eliminates the diagnostic, not the problem. That’s because getchar() must return all possible characters and a distinct EOF value, so a char cannot store its value. We cite this example, perhaps the most common one arising from implementation-defined sign-extension, to show 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.
Bugs 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. Bear in mind that you may be suppressing other diagnostics when you invoke lint with this or any other option. Check the list in 4.6.2 lint Libraries for the options that suppress more than one diagnostic.
A cast of a pointer to one object type to a pointer to an object type with stricter alignment requirements may not be portable. lint flags:
int *fun(y) char *y; { return(int *)y; } |
because, on most machines, an int cannot start on an arbitrary byte boundary, whereas a char can. You can suppress the diagnostic by invoking lint with -h, although, again, you may be disabling other messages. Better still, 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] may be 1 if one compiler is used, 2 if another. 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’) |