ramey@m2.csc.ti.com (Joe Ramey) (04/24/89)
When I run lint on this program main() { try(0); } try(foo) char *foo; { } I get this output: trylint.c: trylint.c(7): warning: argument foo unused in function try try, arg. 1 used inconsistently trylint.c(8) :: trylint.c(3) Why does lint say that the arg. is used inconsistently? I thought that zero could be assigned to any pointer type. Shouldn't lint recognize the constant 0 and realize that it is compatible with (char *) ? Joe Ramey (ti-csl!ramey , ramey@csc.ti.com) TI Computer Science Center
gwyn@smoke.BRL.MIL (Doug Gwyn) (04/25/89)
In article <75688@ti-csl.csc.ti.com> ramey@m2.csc.ti.com (Joe Ramey) writes: >Why does lint say that the arg. is used inconsistently? I thought >that zero could be assigned to any pointer type. Shouldn't lint >recognize the constant 0 and realize that it is compatible with (char *) ? "lint" is correct. With no prototype in scope for the function (always the case for pre-ANSI C), the only conversion applied to function arguments is "default widening", e.g. char to int. The integer constant 0 is passed as an int, which indeed is not compatible with the parameter type later declared for that function.
guy@auspex.auspex.com (Guy Harris) (04/26/89)
>Why does lint say that the arg. is used inconsistently? Because it is. You're passing an integer constant "0" to a routine expecting a "char *". >I thought that zero could be assigned to any pointer type. It can, but that's because in an assignment the compiler knows that the thing being assigned to is a pointer, and therefore that the "0" must be converted to a null pointer constant of the appropriate pointer type. Unless your C compiler supports function prototypes, and unless you use them, the compiler does not know that the "0" matches a formal parameter of pointer type, and therefore cannot do the conversion; you have to explicitly tell it to do so, by casting it to the appropriate pointer type: try((char *)0); (The compiler could, perhaps, in principle know in this particular case that the formal argument is of pointer type; however, in practice, compilers don't do that - I'm not sure whether pre-(d)pANS C specifications permit it to do so, but the (d)pANS explicitly specifies that the compiler should not do so.) If you plan to compile this code only with compilers that support function prototypes (and know for certain that your management isn't ever going to come in the door and ask you to port it to a C implementation that doesn't support them), you can (and should!) use them: void try(char *foo); /* "void", assuming it returns no value */ /* * As long as we're being type-correct... */ /*ARGSUSED*/ int main(int argc, char **argv) { try(0); return 0; } void try(char *foo) { ... } in which case the compiler *will* recognize that the formal argument is of type "char *" and will convert the "0" to a null pointer of type "char *". >Shouldn't lint recognize the constant 0 and realize that it is >compatible with (char *) ? Since the compiler won't do so, no, "lint" shouldn't do so; the code in the form you gave it won't work on, say, machines where a null pointer of type "char *" and an integer with the value 0 don't have the same bit pattern (e.g., a system where an integer is 16 bits long and a "char *' is 32 bits long), and if "lint" didn't warn you about this it would be remiss in its duties.
geoff@cs.warwick.ac.uk (Geoff Rimmer) (04/26/89)
In article <75688@ti-csl.csc.ti.com> ramey@m2.csc.ti.com (Joe Ramey) writes: > When I run lint on this program > > main() > { > try(0); > } > > try(foo) > char *foo; > { > } > > I get this output: > > trylint.c: > trylint.c(7): warning: argument foo unused in function try > try, arg. 1 used inconsistently trylint.c(8) :: trylint.c(3) > > Why does lint say that the arg. is used inconsistently? I thought > that zero could be assigned to any pointer type. Shouldn't lint > recognize the constant 0 and realize that it is compatible with (char *) ? > When lint sees try (0); it says "Hmm. The 0 looks like an integer type to me." So when it comes to the definition of try() and sees the argument is now a "char *", it barfs. If you want lint to know that the argument is actually a "char *", you can do either of the following things: (1) --> declare try() before main(): try (foo) char *foo; {} main() { try(0); } (2) --> cast the 0 to (char *): main() { try ( ( char* ) 0); } try(foo) char *foo; { } (3) --> (the best solution) GET AN ANSI C COMPILER, such as gcc !!! ^^^^^^^^^^^^^^^^^^^^^^ This way, you would code your program thus: void try (char *foo); /* function prototype tells gcc what the argument types are */ int main (void) /* 'void' means no arguments */ { try(0); /* gcc already knows that the argument is (char *) */ } void try (char *foo) { } How did people ever survive without function prototypes! :-) > Joe Ramey (ti-csl!ramey , ramey@csc.ti.com) > TI Computer Science Center Geoff +----------------------------------------------------------+ | GEOFF RIMMER - | | FRIEND OF FAX BOOTHS AND ANSI C | | geoff@uk.ac.warwick.emerald | | Computer Science, Warwick University, England. | | "Gimme a computer and I can do anything" (*) | +----------------------------------------------------------+ (*) as long as UNIX, emacs and gcc are also provided :-)
mat@mole-end.UUCP (Mark A Terribile) (04/27/89)
In article <75688@ti-csl.csc.ti.com>, ramey@m2.csc.ti.com (Joe Ramey) writes: > try(0); > ... > try(foo) > char *foo; > { > trylint.c: > trylint.c(7): warning: argument foo unused in function try > try, arg. 1 used inconsistently trylint.c(8) :: trylint.c(3) > Why does lint say that the arg. is used inconsistently? I thought > that zero could be assigned to any pointer type. ... The problem is that the compiler (or LINT) doesn't know that what must actually be passed is a char* . Thus, the compiler could pass a 16-bit zero to a routine expecting a 32-bit pointer. This is why function prototypes (ANSI C, C++) are better than LINT. (Oh, do I expect a debate on that last statement!) You should write try( (char *) 0 ); in your sample code. -- (This man's opinions are his own.) From mole-end Mark Terribile
kremer@cs.odu.edu (Lloyd Kremer) (04/27/89)
In article <1773@ubu.warwick.UUCP> geoff@cs.warwick.ac.uk (Geoff Rimmer) writes: >How did people ever survive without function prototypes! :-) They coded correctly, casting function arguments when necessary. Prototypes are very helpful in error checking, but it is an unwise practice to *depend* on the compiler to do your casting for you. If a function call would need an explicit cast to work correctly in the absence of a prototype, then it should have an explicit cast. One of the more common errors of this type (pun intended) occurs frequently in UNIX exec calls: execl(path, arg0, arg1, arg2, 0); /* non-portable */ instead of execl(path, arg0, arg1, arg2, (char *)0); -- Lloyd Kremer Brooks Financial Systems ...!uunet!xanth!brooks!lloyd Have terminal...will hack!
geoff@cs.warwick.ac.uk (Geoff Rimmer) (04/29/89)
In article <8660@xanth.cs.odu.edu> kremer@cs.odu.edu (Lloyd Kremer) writes: > In article <1773@ubu.warwick.UUCP> geoff@cs.warwick.ac.uk (Geoff Rimmer) writes: > >How did people ever survive without function prototypes! :-) > > They coded correctly, casting function arguments when necessary. > > Prototypes are very helpful in error checking, but it is an unwise practice > to *depend* on the compiler to do your casting for you. If a function call > would need an explicit cast to work correctly in the absence of a prototype, > then it should have an explicit cast. It was my understanding that confirming ANSI compilers will automatically cast their arguments, if the function has been correctly prototyped. The only exception would be varadic functions, since a prototype like int execl (char *, char *, ...); does not tell the compiler what the types of the optional arguments will be. This means that your execl example: > execl(path, arg0, arg1, arg2, (char *)0); does indeed require a cast even in ANSI C. However, if I have a function that takes a char pointer, and I have told the compiler so by means of a prototype, why can't I then call the function with: func(0); ( or func(NULL); if you prefer) I'm not sure if you're saying this is wrong, or just bad *style*. If ANSI compilers are supposed to cast arguments correctly, then I am perfectly willing to trust my compiler to do so. If I have omitted a prototype, I want to be told so by the compiler. That way I can't lose (except for varadic as mentioned above) - or can I? #include <std/cliche'> > -- > Lloyd Kremer > Brooks Financial Systems > ...!uunet!xanth!brooks!lloyd Geoff /---------------------------------------------------------------\ | GEOFF RIMMER - Friend of fax booths, ANSI C, PCBH, | | phone *numbers* & MPFC & printf | | email : geoff@uk.ac.warwick.emerald | | address : Computer Science Dept, Warwick University, | | Coventry, England. | | PHONE : +44 203 692320 (10 lines) If I'm out please | | leave a message with my secretary. | | FAX : +44 865 726753 | \---------------------------------------------------------------/
guy@auspex.auspex.com (Guy Harris) (05/02/89)
>Prototypes are very helpful in error checking, but it is an unwise practice >to *depend* on the compiler to do your casting for you. Unless you're working with a compiler that supports prototypes, in which case if the compiler won't do the conversions for you, it has what is technically known as a "bug". >If a function call would need an explicit cast to work correctly in >the absence of a prototype, then it should have an explicit cast. That's a stylistic matter - it doesn't *have* to have an explicit case if your compiler supports prototypes, but if it makes you feel better to explicitly cast the argument, go ahead and do so. >One of the more common errors of this type (pun intended) occurs frequently >in UNIX exec calls: > > execl(path, arg0, arg1, arg2, 0); /* non-portable */ > >instead of > > execl(path, arg0, arg1, arg2, (char *)0); ...which is not a good example, since "execl" is a function with a variable number of arguments and, as such, cannot have the types of all its arguments properly described by a prototype - you have to put in an ellipsis. As such, the compiler *cannot* (without extensions beyond the scope of ANSI C) know that all arguments to "execl" should have type "char *", and cannot convert a naked "0" to "(char *)0" in a call. Another common error is setbuf(file, 0); /* or NULL */ which the compiler *can* handle with prototypes, since the prototype for "setbuf" is: void setbuf(FILE *stream, char *buf); so it *does* know that the second argument should have type "char *". The only problem I can see with trusting prototypes if you're never going to use a compiler that doesn't support them is that you might not have the proper prototype in scope. This is a quality-of-implementation issue: the compiler *should* have a mode in which it warns of legal but questionable constructs such as calls to a function with no prototype in scope, non-prototype definitions of functions, etc.. There should also be a "lint" to check for problems that the compiler won't complain about....
bet@dukeac.UUCP (Bennett Todd) (05/03/89)
In article <1794@ubu.warwick.UUCP> geoff@cs.warwick.ac.uk (Geoff Rimmer) writes: >[...] >It was my understanding that confirming ANSI compilers will >automatically cast their arguments, if the function has been correctly >prototyped. >[...] >If ANSI compilers are supposed to cast arguments correctly, then I am >perfectly willing to trust my compiler to do so. If I have omitted a >prototype, I want to be told so by the compiler. I *think* that gcc with the -Wall option should give warnings for any functions called without a prototype in scope; I always compile with gcc using -Wall. I had a large package, containing zillions of functions organized into multiple libraries. I had the library Makefiles generate the library's foo.h file automatically from a template and a sed script which extracted function prototypes from function declarations; I had "make install" install the proper header file in /usr/local/include; I thought I was safe. I called a function which took float parameters, using integer variables for the arguments. I said to myself (very cleverly) "the compiler will generate the cast for me, since I have the proper prototype in scope". KaBOOM! It didn't work. On single-stepping through I found that where I was passing in 128, the function was receiving some ridiculous humongous number. Chagrined, I put in the cast (Rule #1: ALWAYS say exactly what you mean. ALWAYS.). The problem went away. I don't know whether something bizarre happened in my convoluted generation procedure, or whether I tickled some extremely obscure bug in gcc; frankly I don't care. I consider the lesson clear. If the compiler gets it wrong with an explicit cast, then you've got a fatally broken compiler. If what needs to happen can be sufficiently unclear and implicit that humans and/or computers can get confused, then maybe it's best to avoid it? -Bennett bet@orion.mc.duke.edu