turbo-C and lint ?

Derek R. Foster dfoster at jarthur.Claremont.EDU
Sun Mar 25 08:44:11 AEST 1990


In article <1990Mar21.110835.29391 at cs.eur.nl> reino at cs.eur.nl (Reino de Boer) writes:
>alanf at bruce.OZ (Alan Grant Finlay) writes:

>> Consider the following correct program:

>>check(x,y)
>>long x;
>>char *y;
>>{
>>printf("%10.10s ",y);
>>}
>>main()
>>{
>>check(10l,"testing");
>>}
>
>>If you now remove the l after the 10 in the procedure call the compiler
>>issues no relevant warnings and the program misbehaves.  Can Turbo-C 
>>generate a warning for this kind of error?

>[The output from Turbo C with all warnings turned on is compared to
> that of lint. Turbo C does not comment on the call of check involving
> an int.] 

>We can conclude that Turbo-C's checking mechanism does not detect that
>parameter 'x' is used inconsistently,

>Reino R. A. de Boer
>Erasmus University Rotterdam ( Informatica )
>e-mail: reino at cs.eur.nl

The reason that Turbo-C doesn't report an error for this is that for
Turbo-C, it ISN'T an error. For two reasons:

1) when you prototyped

     check(x,y);

   what were are essentially telling Turbo C is

   "I'm going to declare a function, and it takes two arguments, but
    it's none of your business what type those arguments are. Let me
    deal with it."

   So Turbo C respects that request, and does no type checking other
   than to verify that the correct number of arguments are passed.

2) If you had prototyped

     void check(long x, char * y);

   or declared your function as

     void check(long x, char * y) { ... }

   you would have told Turbo C

   "I'm declaring a function that has no return value and takes two
    arguments: a long value and a character pointer. If I try to pass
                                                     ^^^^^^^^^^^^^^^^
    anything else to it, try to convert what I pass to a long and a
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    character pointer, respectively. If you can't, report an error."

You CAN pass an int value to a function which takes a long
parameter. It will simply be sign-extended if necessary, so that
the value that the function recieves as a long represents the
same number that was passed as an int.

Passing a long value to an function which takes an int
parameter will provoke the warning "Conversion may lose
significant digits", and will essentially chop off the
most significant bytes, still attempting to preserve the
sign.

These automagic conversions of values can take place between
many kinds of VALUES (not pointers), including

    char            <-> int

    signed whatever <-> unsigned whatever

    int <-> float
      (I'm not sure about float->int. I think C just truncates
       after the decimal, but I'm not certain. Rounding is also
       possible, or requiring an explicit cast. This is a tricky
       one even under the best of circumstances.)

      (I'm also not sure about int<->enum or int<->typedef int xxx)

Note that while an attempt to send an int to a function which takes
a long parameter is legal, an attempt to send an (int *) to a function
that takes a (long *) is illegal and will provoke a "non-portable
pointer assignment" warning or somesuch, since the compiler can't
really compile conversion code into every statement that might
use indirection on the pointer.

So you see, the reason that Turbo C doesn't complain about your leaving
off the "l" on the constant is (1) because you haven't prototyped
check() properly, and (2) even if you did, Turbo C would simply
convert the int to a long and then end up passing the same value to
check(). It's not that Turbo C has inadequate error-CHECKING abilities.
Instead, it has greater potential error HANDLING.

Side note:
This is also (I think) a reason why the value NULL should be able to
be passed to functions which take other than (void *) parameters, without
explicit casting. If the function is prototyped properly, the compiler
should recognize that since NULL has type (void *) and the function accepts
a (char *), for instance, that a conversion may be necessary if the
value "NULL" in a (char *) is different from that in a (void *). Or why
you don't need to say memcpy((void *) &x, (void *) &y, len), as someone
has suggested recently. The (void *) cast is implicit in the prototype of
memcpy as "void * memcpy(void * x, void * y, size_t z)" (or something
similar -- this is off the top of my head.), and the compiler shouldn't
even issue a warning for implied casts to and from a (void *). Passing a
(char *) to an (int *) will probably give you a warning message, but it can
STILL technically be handled, even if the representations of the pointers
are different. (I would not suggest you do this, however! It is rather
nonportable! Anything to/from a void * should be OK, however)

BTW, I would like to know how much of this if this is standard behavior for
ANSI C, and how much is Turbo C extensions. What does the standard say?

I hope this clears up some confusion!

Derek Riippa Foster



More information about the Comp.lang.c mailing list