[net.lang.c] icky C code

chris@umcp-cs.UUCP (07/17/86)

In article <2239@brl-smoke.ARPA> mangoe@mimsy.umd.edu (Charley Wingate) writes:
>... I've been looking at 4.3 code, particularly for the Sun.  There
>are plenty of places where pointers are used indifferently as
>pointers to structures and as pointers to arrays.

If you mean `pointers to structures and pointers to arrays of those same
structures', there is nothing wrong with that.  The C memory model demands
that an array be a linear arrangement of its components.  If this is not
true on a machine, that machine does not run C code.

>There is code which relies on ints and pointers being the same size.

Alas, this seems to creep in everywhere---usually as a result of
programmer laziness.  An imagined programming session:

	/* Oops, gotta call getenv, but the declarations are all
	   way above.  Enh, I won't bother declaring it. */
	p = (char *) getenv("FOO");

First, this is bad coding, like writing unnecessary LOOPHOLEs in Mesa.
Second, lint will complain.  Third, it is not really all that hard to
go put in the declaration.  Leave out the cast and even the compiler
will complain, which will remind you to go clean up!

>There is code which relies on successive declarations being stored
>contiguously and in order.

Where?

>Code which relies on NULL equalling zero is omnipresent.

But NULL *must* equal zero.  If you mean `the bit pattern of the
null pointer-to-X equalling the bit pattern of the zero integer',
again, this is bad coding, and again, lint will complain.

>All this leads me inescapably to the conclusion that C was designed
>to allow programmers to violate the rules which everyone else is
>busily putting into their languages to protect themselves.

In 1978 (or perhaps earlier, but the documentation says July 1978),
Steven C. Johnson recognised the need for a type-checker for the
C language.  He wrote one.  It is called lint.  If you think
type-checking is good, *use it*.  If you just want your program
to work on your Widget-brand computer, do whatever you like---
though you might still find lint useful.

There are those who consider `lint' a necessary part of the C
compiler, that the proper compilation command is

	lint file.c; cc file.c -o file

These people never seem to worry about all the problems you describe.
The C language has a good type-checker.  It is just not built into
the `normal' compiler.  This may be a serious problem to some, but
I have never found it to be so.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 1516)
UUCP:	seismo!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@mimsy.umd.edu

chris@umcp-cs.UUCP (07/17/86)

In article <2446@umcp-cs.UUCP> I wrote:
>In 1978 (or perhaps earlier, but the documentation says July 1978),
>Steven C. Johnson recognised the need for a type-checker for the
>C language. ...

Ai!  Make that `Stephen C. Johnson.'  ---Or perhaps `S. C. Johnson',
as it says in the documentation.  My apologies for misspelling your
name.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 1516)
UUCP:	seismo!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@mimsy.umd.edu

greg@utcsri.UUCP (Gregory Smith) (07/17/86)

In article <2446@umcp-cs.UUCP> chris@maryland.UUCP (Chris Torek) writes:
>>There is code which relies on ints and pointers being the same size.
>
>Alas, this seems to creep in everywhere---usually as a result of
>programmer laziness.  An imagined programming session:
>
>	/* Oops, gotta call getenv, but the declarations are all
>	   way above.  Enh, I won't bother declaring it. */
>	p = (char *) getenv("FOO");
>
>First, this is bad coding, like writing unnecessary LOOPHOLEs in Mesa.
>Second, lint will complain.  Third, it is not really all that hard to
>go put in the declaration.  Leave out the cast and even the compiler
>will complain, which will remind you to go clean up!
>
Fourth, it is not guaranteed to be portable. It will probably be a lot more
portable than it should be, though. Compiler implementors have gone to
surprising lengths to allow this sort of thing. From a code efficiency
standpoint, for example, a 68000 should return int's in D0 and all pointer
types in A0, which would break the above. ( Somebody pointed out that a
certain 68K compiler has an option to return both types in *both* regs ).
Or am I wrong, and there is a non-unwritten ( i.e. official ) rule that
says everything should return in the same reg whenever possible?

Imagine a library function ( coded in assembler ) which returns a useful
int value if declared int and a useful, different, pointer value if declared
as a pointer! ( not a serious suggestion, but would be feasible if the
same code were given two names ).

-- 
"You'll need more than a Tylenol if you don't tell me where my father is!"
						- The Ice Pirates
----------------------------------------------------------------------
Greg Smith     University of Toronto      UUCP: ..utzoo!utcsri!greg

jdl@purdue.UUCP (Jon Loeliger) (07/30/86)

In article <3117@utcsri.UUCP>, greg@utcsri.UUCP (Gregory Smith) writes:
> From a code efficiency
> standpoint, for example, a 68000 should return int's in D0 and all pointer
> types in A0, which would break the above. ( Somebody pointed out that a
> certain 68K compiler has an option to return both types in *both* regs ).
> Or am I wrong, and there is a non-unwritten ( i.e. official ) rule that
> says everything should return in the same reg whenever possible?


I am in the process of writing a 68000 backend and just
this problem arose.  As of now, I resolved it by postponing
the critical decision, and left everything in D0 upon return.
------------------------------------------------------------------------------
Jon Loeliger		I *think* these are my opinions, let me ask my boss...
jdl@Purdue.ARPA					Dept Computer Science
jdl@Purdue.Edu					W. Lafayette, IN 47907
jdl@mordred.cs.purdue.edu			(317) 494-6180
{ihnp4, decvax, ucbvax, pur-ee}!purdue!jdl
-- 
------------------------------------------------------------------------------
Jon Loeliger		I *think* these are my opinions, let me ask my boss...

jdl@Purdue.ARPA					Dept Computer Science
jdl@Purdue.Edu					W. Lafayette, IN 47907
jdl@mordred.cs.purdue.edu			(317) 494-6180
{ihnp4, decvax, ucbvax, pur-ee}!purdue!jdl

cg@myrias.UUCP (Chris Gray) (07/31/86)

Jon Loeliger at Purdue writes that he needs to decide whether address results
on a 68000 compiler should be in A0 or D0. Unfortunately, the best answer is
probably obtained by looking at any code that you have to interface to - what
does it do? I ran into the exact same problem while porting my Draco compiler
to the Amiga. I planned to return address values in A0, so that they could
be used directly if that's all the code called for. All of the AmigaDos and
Kernal routines return all results in D0 however. This left me with three
choices: have interface stubs move the result to A0; require very strange
declarations and type cheating for the supplied routines; or have the compiler
return pointers in D0 as well. For now I've chosen the third alternative, but
I may eventually go back to the first.

			Chris Gray (..ihnp4!alberta!myrias!cg)

crowl@rochester.ARPA (Lawrence Crowl) (08/02/86)

In article <3117@utcsri.UUCP>, greg@utcsri.UUCP (Gregory Smith) writes:
> From a code efficiency
> standpoint, for example, a 68000 should return int's in D0 and all pointer
> types in A0, which would break the above. ( Somebody pointed out that a
> certain 68K compiler has an option to return both types in *both* regs ).
> Or am I wrong, and there is a non-unwritten ( i.e. official ) rule that
> says everything should return in the same reg whenever possible?

Since there is no type checking between an external definition of a function
and its actual definition, a programmer could implicitly type cast by listing
the external definition with a different return type than the actual.  If
integers are put in D0 and addresses in A0, then the calling function would
grab the return value from the wrong register.

extern int f() ;
                               BUT
usage( ) { int i = f() ; }             char *f() { return "ptr" ; }
/* grabs from D0 */                    /* puts in A0 */

So, the optimization would probably break some programs.
-- 
Lawrence Crowl			716-275-5766	University of Rochester
			crowl@rochester.arpa	Computer Science Department
 ...!{allegra,decvax,seismo}!rochester!crowl	Rochester, New York,  14627

guy@sun.uucp (Guy Harris) (08/03/86)

> extern int f() ;
>                                BUT
> usage( ) { int i = f() ; }             char *f() { return "ptr" ; }
> /* grabs from D0 */                    /* puts in A0 */
> 
> So, the optimization would probably break some programs.

Those programs are already broken; it's just by pure luck that they happend
to work on some C implementations.  They also won't work on any C
implementation where "sizeof(char *)" is different from "sizeof(int)";
nothing forbids such an implementation.  "lint" will catch illegal code like
the example above.

It would be nice if compilers started giving warnings for using undeclared
functions, and if the "a function can be implicitly declared as 'int' by
usage" rule were deprecated by the ANSI standard.  Lazily-written code that
doesn't declare functions would still compiler under those compilers, and
work as well as it did before (which may mean "work just fine" and may mean
"not work at all"), but at least the programmer would be told that they may
be doing something wrong.
-- 
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com (or guy@sun.arpa)

greg@utcsri.UUCP (Gregory Smith) (08/04/86)

In article <19880@rochester.ARPA> crowl@rochtest.UUCP (Lawrence Crowl) writes:
>In article <3117@utcsri.UUCP>, greg@utcsri.UUCP (Gregory Smith) writes:
>> From a code efficiency
>> standpoint, for example, a 68000 should return int's in D0 and all pointer
>> types in A0, which would break the above. ( Somebody pointed out that a
>> certain 68K compiler has an option to return both types in *both* regs ).
>
>Since there is no type checking between an external definition of a function

	There is lint, which does this.

>and its actual definition, a programmer could implicitly type cast by listing
>the external definition with a different return type than the actual.  If
>integers are put in D0 and addresses in A0, then the calling function would
>grab the return value from the wrong register.
>
>So, the optimization would probably break some programs.

Very likely, but only incorrect ones. This 'implicit casting' is not
legal C. It is much the same as passing an int to a function which
expects a struct containing a single int - it will usually work, but if
(when) it doesn't, the programmer is at fault. Would you call that
'implicit casting'?

 I assert that a 68K C implementation could be made which returns
pointers in A0 and ints in D0, and no lintable program would be broken.
(If this is not the case, then lint needs work :-) )


-- 
"You'll need more than a Tylenol if you don't tell me where my father is!"
						- The Ice Pirates
----------------------------------------------------------------------
Greg Smith     University of Toronto      UUCP: ..utzoo!utcsri!greg