The REAL problem(?) with C.

Gregory Smith greg at utcsri.UUCP
Thu Feb 20 07:29:38 AEST 1986


In article <1251 at watmath.UUCP>
rbutterworth at watmath.UUCP (Ray Butterworth) writes:
>I wonder how many tens of thousands of dollars of time and resources
>have been wasted recently by all of these complaints and discussions
>about what the % operator should do.
>
>The real problem of course isn't that the % operator was definded
>correctly or incorrectly, the problem is that it is intentionally
>poorly defined in C.  The expression -5%2 will be positive on some
>implementations, and negative on others, but in both cases the answer
>will be "correct".
The nail has been finally hit on the head.

I see C as about the closest thing we have to a 'portable' assembler,
for the following reasons ( among others ):

	- The language has 'machine' operators like >>,<<, &,|,~
	  and pointer operations, available to the user.

	- C does not 'hide' the fact that a computer memory consists
          of a linear homogenous  address space (except maybe 8086 >:-) )
	  The C programmer normally has to be aware of this. In most other
	  high-level languages, this is not the case.

	- C does not hide the fact that machines are different from each
	  other. It tries to, but it does not. The programmer needs to
	  be very much aware of the nature of these differences to be
	  able to write portable code. This is not the case in most other
	  HLLs.

Now the point.
If C is to be an 'assembler' or 'high-level-assembler', it should
be able to produce very efficient code, to eliminate one of the prime
reasons for using 'real' assembler.  For the produced code to be very
efficient, it should be as near as possible to the bare instructions required
to do what needs doing. Now it is a hard fact that machines have hardware
'mod' and 'div' operators, and different machines produce different results
for these. I do not think it would be a good idea to standardize the
results of / and %, because then machines which did not produce the 'correct'
results would have problems. The compiler would have to emit clumsy sign-
checking code, or call a routine to do the operation. This is not consistent
with the 'efficiency' objective.

I believe the designers of C were thinking exactly along these lines.
More evidence: char to int conversion may or may not be sign-extended.
The PDP-11 makes it dashed clumsy to move a char to an int *without*
sign-extend, thus PDP-11 C sign-extends. If the machine cannot do this
extension easily, the no-extend operation should take place. More:
some types ( unsigned long, unsigned char ) may not be allowed.

If you need a certain type of division/mod/whatever not guaranteed, do
one of the following:
	(1) write a non-portable version, if you know it will never
	    need to be ported.
	(2) use #defines:  #define div(a,b) ((a)/(b))   on one machine,
			   #define div(a,b) ((a)<0?-(-(a)/(b)):(a)/(b))
						 on another, etc
	(3) Don't use C   :-).
Note that option 2 corresponds to making the compiler emit code for
machines with the 'wrong' divide operation. Except this way you
only get it when you need it.

Macros like this could even be provided in system-dependant header files,
to provide commonly used 'guaranteed' operations. Like toupper(c) and
things like that.

Greg Smith
University of Toronto
---------------------------------------------------------------------
These opinions are mine and are not necessarily those of anybody else
nor are they necessarily very original!      ( =-> )



More information about the Comp.lang.c mailing list