Path: utzoo!attcan!uunet!samsung!zaphod.mps.ohio-state.edu!mips!bridge2!jarthur!dfoster From: dfoster@jarthur.Claremont.EDU (Derek R. Foster) Newsgroups: comp.lang.c Subject: Re: turbo-C and lint ? Keywords: PD lint Message-ID: <5407@jarthur.Claremont.EDU> Date: 24 Mar 90 22:44:11 GMT References: <1964@bruce.OZ> <1990Mar20.130947.16583@cs.eur.nl> <1966@bruce.OZ> <1990Mar21.110835.29391@cs.eur.nl> Organization: Harvey Mudd College, Claremont, CA 91711 Lines: 127 In article <1990Mar21.110835.29391@cs.eur.nl> reino@cs.eur.nl (Reino de Boer) writes: >alanf@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@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