[comp.lang.c] Incrementing after a cast

kanner@apple.UUCP (Herbert Kanner) (12/03/86)

We would like to solicit some opinions on the legality of the
following, which is one way of implementing varargs for, e.g., printf:

	foo()
	{
		sometype L;
		char *chp;

		L = *((sometype *) chp)++;
	}

Both the portable C compiler and lint happily accept this construct.
The idea of the cast is to force chp to be incremented by the size of
sometype.

Our problem arises with the allegation that K&R makes this construct
illegal.  On page 214 (syntax summary), the only legal context for ++
is given to be lvalue++, and (type-name) expression is a case of
expression which, according to that syntax, cannot be an lvalue.
-- 
Herb Kanner
Apple Computer, Inc.
{idi, ios, nsc}!apple!kanner

henry@utzoo.UUCP (Henry Spencer) (12/04/86)

>		char *chp;
>		L = *((sometype *) chp)++;
>
>The idea of the cast is to force chp to be incremented by the size of
>sometype.  [But K&R says it's illegal.]

It is illegal.  There are messier ways of getting around that, but you are
making a fundamental mistake regardless.  What you are saying is "treat chp
as a sometype pointer, and increment it".  There is no guarantee that the
"increment a sometype pointer" operation bears any relation to the "add n
to a char pointer" operation.  The two kinds of pointers may not even be
the same size!

If you wish to do this cleanly, the following is not guaranteed but is more
likely to do the right thing on a wide variety of machines:

		char *chp;
		L = *(sometype *)chp; chp += sizeof(sometype);
-- 
				Henry Spencer @ U of Toronto Zoology
				{allegra,ihnp4,decvax,pyramid}!utzoo!henry

garry@batcomputer.tn.cornell.edu (Garry Wiegand) (12/05/86)

In a recent article kanner@apple.UUCP (Herbert Kanner) wrote:
>...
>		L = *((sometype *) chp)++;
>...
>Our problem arises with the allegation that K&R makes this construct
>illegal...

This was some discussion on the net a few months about whether this ought
to be legitimate.  The context I would use it in is trying to recycle
"valuable" register variables to point to different things during the life
of a routine. For example, if I happen to know that A and B never overlap
during execution, and that my machine only has 1 available-to-C register,
I might want to do:

	    ...
	    register	char	*A;
#           define	B	((float *)A)
	    ...

and expect both *A++ and *B++ to work and to be in registers for me. If
I know that a pointer and an int take the same space and instructions on
my machine I might also want to get away with:

	    register	char	*A;
#	    define	B	((int)A)
	    ...
	    ... *A++; ...
	    ...
	    ... B = 27;

Unfortunately, that's not the way things stand on any compiler I have access
to - your compiler must be exceptionally forgiving.

I think the new spec doesn't disturb the status quo.

garry wiegand   (garry%cadif-oak@cu-arpa.cs.cornell.edu)

bds@mtgzz.UUCP (12/05/86)

In article <349@apple.UUCP>, kanner@apple.UUCP (Herbert Kanner) writes:
> 	foo()
> 	{
> 		sometype L;
> 		char *chp;
> 
> 		L = *((sometype *) chp)++;
> 	}
> ...
> Our problem arises with the allegation that K&R makes this construct
> illegal.  On page 214 (syntax summary), the only legal context for ++
> is given to be lvalue++, and (type-name) expression is a case of
> expression which, according to that syntax, cannot be an lvalue.

Your confusion comes, I believe, by assuming that precedence and
grouping rules force one parse to the exclusion of all others
(i.e. that the expression is parsed as *(((sometype *) chp)++) which
is illegal). By parenthesising the expression:

	L = (*((sometype *) chp))++

Then using the rules on page 214 the parse is:

	expression : (type-name) expression
	lvalue : * expression
	expression : lvalue++

Note that this is the parse generated even without the parenthesis.
Happily, it is the one you want.

braner@batcomputer.tn.cornell.edu (braner) (12/06/86)

[]

If  ((sometype *)pointer)++ is not legal, then the law should be changed!
(since the pointer-type cast is legal, why can't you use the resulting
pointer like any pointer of sometype?)

BUT, if you cast it back, as in varargs with args of various types,
then some machines supposedly give you problems.  But the implementations
should avoid problems by agreeing that:

	consecutive things are stored upwards in memory,

	incrementing a pointer points to the next memory slot,

	casting to a pointer to a type with stricter alignment
	will be rounded UP to fit.  (This has to be done anyway
	when figuring out how to store the stuff.  For example,
	if you push a byte on the 68000 stack a whole word is used.)

As for recycling registers, using the same register for different
types in the same function:  I think that would be very valuable.
How about allowing something like this:

	register union {int i; long j;} x;

If your machine makes it impossible, the compiler can separate them
into separate registers if available, or (as is anyway the case with
register variables) make it non-register.  So no harm done!  To get
the best results, on some machines you would be advised NOT to unionize
int/longs and pointers.

- Moshe Braner

chris@mimsy.UUCP (Chris Torek) (12/06/86)

>In article <349@apple.UUCP> kanner@apple.UUCP (Herbert Kanner) writes:
>>		L = *((sometype *) chp)++;
[is illegal---correct]

In article <2319@mtgzz.UUCP> bds@mtgzz.UUCP writes:
>Your confusion comes, I believe, by assuming that precedence and
>grouping rules force one parse to the exclusion of all others

That is a reasonable assumption, for they do.

>By parenthesising the expression:
>
>	L = (*((sometype *) chp))++

This is indeed legal.  It is also not what was written, and not what
was desired.

>Note that this is the parse generated even without the parenthesis.

No: casts and `++' have higher precedence than `*'.  The construct
is semantically illegal, but the compiler will not alter it because
of this.

Incidentally, with a more concrete example:

	int L;
	char *cp;

	L = (*(int *)cp)++;

treats `cp' as though it were a pointer to an integer, obtains the
integer to which that points, copies that value into L, then increments
the integer to which that points.  The code generated is roughly:

	mov	cp,reg
	mov	*reg,L
	add	#1,*reg
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
UUCP:	seismo!mimsy!chris	ARPA/CSNet:	chris@mimsy.umd.edu

blarson@usc-oberon.USC.EDU (Bob Larson) (12/06/86)

In article <1706@batcomputer.tn.cornell.edu> garry%cadif-oak@cu-arpa.cs.cornell.edu writes:
>This was some discussion on the net a few months about whether this ought
>to be legitimate.  The context I would use it in is trying to recycle
>"valuable" register variables to point to different things during the life
>of a routine. For example, if I happen to know that A and B never overlap
>during execution, and that my machine only has 1 available-to-C register,
>I might want to do:
>
>	    ...
>	    register	char	*A;
>#           define	B	((float *)A)
>	    ...
>
>and expect both *A++ and *B++ to work and to be in registers for me. If
>I know that a pointer and an int take the same space and instructions on
>my machine I might also want to get away with:
>
>	    register	char	*A;
>#	    define	B	((int)A)
>	    ...
>	    ... *A++; ...
>	    ...
>	    ... B = 27;
>
>Unfortunately, that's not the way things stand on any compiler I have access
>to - your compiler must be exceptionally forgiving.

I'm glad none of the compilers I use allow such illegal constructs.
If you are realy that worried about register usage, I know of at least
two solutions that are leagal C and do not break on compilers that
don't support them:

1.  A compiler which does it's own register assignment based on the
code and ignores the "register" advisory on declarations.  Yes, I realy
do use such a compiler:  Prime C 5.0-19.4 with the -32ix and -optimize
1 options and no -noignoreregister option.  (Yes, the
-noignoreregister option does what you expect, it just produces worse
code in the majority of cases.)

2.  A compiler that properly supports declareation scopes, (I'm sure
there are some that don't, but how common it is I don't know) and
writing the code like:

	    ...
	    {
		register	char	*A;
		...
	    }
	    {
	    	register	float	*B;
		...
	    }
	    ...

Any reasonable compiler should use the same register for A and B iff
apropriate.  This also works in the case of int B, however the same
register will not be used if it is not appropriate.  (i.e. on a 68000
compiler that uses address registers for register pointer variables
and data registers for register int variables.)  Unless your compiler
is broken, this code will work even if the compiler isn't reasonable.

>I think the new spec doesn't disturb the status quo.

As much as possible, the ANSI C proposed standard tries to avoid
making such clearly non-portable code legal.  I wish they had also
made the type of quoted strings const char [] rather than char [], but
I do realize this would break some (in my optionion questionable) code.
The Prime C compiler has options to force this, but the os9/68k
compiler realy needs it.

>garry wiegand   (garry%cadif-oak@cu-arpa.cs.cornell.edu)


-- 
Bob Larson
Arpa: blarson@usc-oberon.arpa	or	Blarson@Usc-Eclb.Arpa
Uucp: (several backbone sites)!sdcrdcf!usc-oberon!blarson
			seismo!cit-vax!usc-oberon!blarson

throopw@dg_rtp.UUCP (Wayne Throop) (12/06/86)

> kanner@apple.UUCP (Herbert Kanner)
> We would like to solicit some opinions on the legality of [...]
>         *((sometype *) chp)++;

Well, I think it's illegal, Kernighan thinks it's illegal, Ritchie
thinks it's illegal, Harbison thinks it's illegal, Steele thinks it's
illegal, and (I would venture to guess) all the members of the X3J11
ANSI C group think it is illegal.  Is that enough "opinions" for you?
Will you now agree that it is Not a Very Good Idea?  Go ahead!  Argue
the point!  Make my day!

More seriously, the *reason* it is illegal is that casts (at least
conceptually, and in many cases actually) *DO* *SOMETHING* to the value
they are applied to.  Would you expect ((-i)++) to work?  Would you
expect ((i+j)++) to work?  More relevant, would you expect
(((float)i)++) to work?  I would *NOT* expect them to work, and
similarly I do not expect (((some_type *)p)++) to work, either.

> Both the portable C compiler and lint happily accept this construct.

"This turns out not to be the case."

Given this input:
        1  char *p;
        2  void f(){
        3      ((int *)p)++;
        4  }
lint on our system says (among other things):
        (3)  illegal lhs of assignment operator

If your lint doesn't complain about it, it is buggy.

> The idea of the cast is to force chp to be incremented by the size of
> sometype.

Sure.  OK.  Fine.  But that isn't the way to accomplish this.  Try

        L = *(sometype *)chp;
        chp = (char *)(((sometype *)chp)+1)

It works just as well, and has the advantage of being legal C.  Still
legal but less portable is to declare chp to be a union of all the
relevant pointer types like so:

    union {sometype *stp; anothertype *atp; someothertype *sotp; } p;

and use something like

        somevalue = *p.stp++;

to do the save-value-and-increment-pointer operation.  Note that this
only works on machines where the various pointer formats are identical,
and where the compiler implements unions "the way you expect".

--
"Happily, I read English."
(Draws sword) "Then read it happily!"
                --- Exchange in the 1950s production of IVANHOE
-- 
Wayne Throop      <the-known-world>!mcnc!rti-sel!dg_rtp!throopw

ballou@brahms (Kenneth R. Ballou) (12/07/86)

In article <1746@batcomputer.tn.cornell.edu> braner@batcomputer.UUCP (braner) writes:

>If  ((sometype *)pointer)++ is not legal, then the law should be changed!

Why?

>(since the pointer-type cast is legal, why can't you use the resulting
>pointer like any pointer of sometype?)

You can use the *VALUE* of the pointer in exactly the same way as you could
use the *VALUE* of any other pointer.  Keep in mind that the pre/post-increment
operator requires one of those mythical beasts, an l-value.  Likewise, you
could not apply pre/post-increment to the result of a function call.

braner@batcomputer.tn.cornell.edu (braner) (12/08/86)

[]

In article <1746@batcomputer.tn.cornell.edu>  I wrote:
>If  ((sometype *)pointer)++ is not legal, then the law should be changed!
>(since the pointer-type cast is legal, why can't you use the resulting
>pointer like any pointer of sometype?)

In article <491@cartan.Berkeley.EDU> ballou@brahms (Kenneth R. Ballou) wrote:
>You can use the *VALUE* of the pointer in exactly the same way as you could
>use the *VALUE* of any other pointer. ...the pre/post-increment
>operator requires one of those mythical beasts, an l-value.  Likewise, you
>could not apply pre/post-increment to the result of a function call.

- When you cast a pointer to a pointer of another type, you are telling
the compiler to use it differently (e.g. to read 2 instead of 1 byte
from memory when dereferencing).  Since the compiler is aware of the cast
in that sense, it COULD increment it according to the new type's size!
I see no TECHNICAL obstacle here, only "legal" morass...

- Moshe Braner

chris@mimsy.UUCP (Chris Torek) (12/08/86)

In article <4674@mimsy.UUCP>, in reference to `L = *((int *)cp)++', I wrote:
>casts and `++' have higher precedence than `*'.

`Wrongo, fishbreath.'  Sorry about that.  Casts, ++, and unary * all
have the same precedence; but they group right-to-left, not left-to-
right.

Thanks to Roger Hale for pointing this one out.  I wonder how I managed
that kind of slip?
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
UUCP:	seismo!mimsy!chris	ARPA/CSNet:	chris@mimsy.umd.edu

ballou@brahms (Kenneth R. Ballou) (12/08/86)

In article <1762@batcomputer.tn.cornell.edu> braner@batcomputer.UUCP (braner) writes:
>[]
>
>In article <1746@batcomputer.tn.cornell.edu>  I wrote:
>>If  ((sometype *)pointer)++ is not legal, then the law should be changed!
>>(since the pointer-type cast is legal, why can't you use the resulting
>>pointer like any pointer of sometype?)
>
>In article <491@cartan.Berkeley.EDU> ballou@brahms (Kenneth R. Ballou) wrote:
>>You can use the *VALUE* of the pointer in exactly the same way as you could
>>use the *VALUE* of any other pointer. ...the pre/post-increment
>>operator requires one of those mythical beasts, an l-value.  Likewise, you
>>could not apply pre/post-increment to the result of a function call.
>
>- When you cast a pointer to a pointer of another type, you are telling
>the compiler to use it

Thank you.  Exactly what is *it*?  *It* is the VALUE of the pointer.  This is
what is called "r-value" in Kernighan and Ritchie, and in Harbison and Steele.

> 			  differently (e.g. to read 2 instead of 1 byte
>from memory when dereferencing).  Since the compiler is aware of the cast
>in that sense, it COULD increment it according to the new type's size!

WRONG.  Do you have any clue what the difference is between "l-value" and
"r-value"?  A CAST DOES NOT PRODUCE AN L-VALUE!  I quote from Kernighan and
Ritchie, page 187:

	    An expression preceded by the parenthesized name of a data
	type causes conversion of the value of the expression to the
	named type.  This construction is called a 'cast.'

Then again, here is another quotation from Harbison and Steele, page 152:

	    The cast causes the operand value to be converted to the
	type named within the parentheses.  Any permissible conversion
	may be invoked by a cast expression.  The result is not an
	lvalue.

>I see no TECHNICAL obstacle here, only "legal" morass...

	May I then suggest you look again.  It seems to me that if something
is not an l-value, the compiler is not to use it as an l-value.

Perhaps I could put in a good word with Santa Claus and ask him to leave
you a copy of K&R, or maybe even Harbison & Steele?

--------
Kenneth R. Ballou		ARPA: ballou@brahms.berkeley.edu
Department of Mathematics	UUCP: ...!ucbvax!brahms!ballou
University of California
Berkeley, California  94720

m5d@bobkat.UUCP (Mike McNally ) (12/09/86)

In article <1746@batcomputer.tn.cornell.edu> braner@batcomputer.UUCP (braner) writes:
>[]
>
>If  ((sometype *)pointer)++ is not legal, then the law should be changed!
>(since the pointer-type cast is legal, why can't you use the resulting
>pointer like any pointer of sometype?)
>

You can use the pointer like any other pointer *value*.  However, as
with any construction resulting in an rvalue, the result of a cast
cannot be autoincremented; it has no address.  As many have pointed out
in this group over the past couple of months, on many machines the
casting of one pointer type to another requires that the value be
modified in some way.

>BUT, if you cast it back, as in varargs with args of various types,
>then some machines supposedly give you problems.  But the implementations
>should avoid problems by agreeing that:
>
>	consecutive things are stored upwards in memory,

What??  What if the "things" are of different types, and the
architecture of the machine favors storage of different types in
different segments?  What other high-level language guarantees that
separately-declared objects are stored consecutively? (I *know* that
dozens of people know of dozens of languages for which such assumptions
are valid; I don't want to hear about it.)

>	incrementing a pointer points to the next memory slot,

What is a memory slot?  In the language I use, C, when a pointer is
incremented, it is incremented;  afterwards it points to whatever it
points to.  The amount by which the pointer is incremented is equal to
the size of the type pointed to.  I guess I must be confused.

>	casting to a pointer to a type with stricter alignment
>	will be rounded UP to fit.  (This has to be done anyway
>	when figuring out how to store the stuff.  For example,
>	if you push a byte on the 68000 stack a whole word is used.)
>

Rounded UP?  Why not down?  Then, if I cast a pointer-to-char into a
pointer-to-long, the long will contain the char.  Actually, I don't
think any rounding should be done.  The semantics would be too machine-
dependent to be of any use in a program.  What would it mean anyway?
This is too bizarre for me.

>	[stuff about register variables]
>- Moshe Braner

I think that further suggestions along the lines of "let's change the
language so I can do something disallowed for good reason in the
existing language" should be directed to the ANSI committee, where they
hopefully will be directed to the ANSI wastebasket.

-- 
****                                                         ****
**** At Digital Lynx, we're almost in Garland, but not quite ****
****                                                         ****

Mike McNally                                    Digital Lynx Inc.
Software (not hardware) Person                  Dallas  TX  75243
uucp: ...convex!ctvax!bobkat!m5                 (214) 238-7474

mouse@mcgill-vision.UUCP (der Mouse) (12/09/86)

In article <2319@mtgzz.UUCP>, bds@mtgzz.UUCP writes:
> In article <349@apple.UUCP>, kanner@apple.UUCP (Herbert Kanner) writes:
>> 		L = *((sometype *) chp)++;
>> Our problem arises with the allegation that K&R makes this construct
>> illegal.
> By parenthesising the expression:
> 	L = (*((sometype *) chp))++
> Then [it's legal].
> Happily, it is the one you want.

I certainly hope not.  The intent was clearly to increment chp by
sizeof(sometype), or something very much like that.  Your proposed
parse results in an expression that doesn't touch chp; even *it* won't
be legal unless "sometype" can have ++ applied to it - for it's
something of type "sometype" that it's ++ing.

You also mention (I deleted it) that this parse is generated even
without the parentheses.  Well, I don't know what compiler you're
using.  Here is what my machine (VAX750/MtXinu4.3+NFS) gives:

	% cat x.c
	main()
	{
	 int i;
	 int j;
	 char *cp;
	
	 cp = (char *) &i;
	 j = *((int *)cp)++;
	}
	% cc x.c
	"x.c", line 8: illegal lhs of assignment operator
	%

					der Mouse

USA: {ihnp4,decvax,akgua,utzoo,etc}!utcsri!mcgill-vision!mouse
     think!mosart!mcgill-vision!mouse
Europe: mcvax!decvax!utcsri!mcgill-vision!mouse
ARPAnet: think!mosart!mcgill-vision!mouse@harvard.harvard.edu

[USA NSA food: terrorist, cryptography, DES, drugs, CIA, secret, decode]

karl@haddock.UUCP (Karl Heuer) (12/10/86)

In article <1746@batcomputer.tn.cornell.edu> braner@batcomputer.UUCP (braner) writes:
>[when using the (illegal) expression ((sometype *)pointer)++,] some machines
>supposedly give you problems.  But the implementations should avoid problems
>by agreeing that: ... casting to a pointer to a type with stricter alignment
>will be rounded UP to fit.

No.  If the machine has to do extra work to guarantee proper alignment, then
I don't want it done.  (In *my* code, (sometype *)charpointer only appears
if I already know charpointer is aligned.)

>(This has to be done anyway when figuring out how to store the stuff.  For
>example, if you push a byte on the 68000 stack a whole word is used.)

I don't think this is relevant.  If the architecture requires (or the compiler
prefers) the stack pointer to always be aligned, then push(byte) may have to
be compiled into movb(byte, *--sp) and clrb(*--sp), but here the adjustment is
by a constant amount known at compile-time.

>As for recycling registers, using the same register for different
>types in the same function:  I think that would be very valuable.
>How about allowing something like this:
>	register union {int i; long j;} x;

As far as I know, this is legal C.  However, many (most?) compilers don't
believe that a struct/union can fit in a register*, and will treat it as
"auto"; on such a machine you sacrifice both readability and efficiency.

I try to declare register variables in the smallest possible scope, but
sometimes the lexical scopes still overlap even though the useful lifespans
do not.  If I'm really concerned about efficiency, I'll hand-optimize the
asm file.

I predict there will never be an official extension to C to "fix" this.
Some compilers already optimize register usage; soon this will be expected
behavior, and the "register" keyword will be obsolete.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
*It would be nice if more compilers would make a special case out of "small
struct/union".  In particular, a function returning union{int;char*} should
not have to use the general "hidden fake arg" mechanism.

braner@batcomputer.UUCP (12/10/86)

[]

I tried to be brief and got dumped on...

When I suggested a standard that "things be stored upwards in memory",
I meant inside arrays and as arguments passed to a function (on the stack,
usually).  If things were not stored that way, we wouldn't be talking
about incrementing a pointer as adding sizeof(...) to the address!

In the case where the "things" are of different types, as in the arguments-
on-the-stack case, their position must anyway be adjusted to fit the
alignment requirements of each type.  I gave the example of pushing a byte
on the stack on the 68000: a word (two bytes) is pushed to preserve
the even-ness of the stack pointer.  This could allow some reasonable
way to do "varargs".  It can also cause problems:  adding sizeof(char)
to a pointer used to access the stacked stuff will NOT work right on
the 68000, due to the above-mentioned automatic alignment.  This may
or may not be related to K&R introducing the automatic cast into an int
for calculations (and function calls) involving chars...

(If it still doesn't make any sense to you, Mike, it's YOUR problem :-)

- Moshe Braner

greg@utcsri.UUCP (Gregory Smith) (12/12/86)

In article <190@haddock.UUCP> karl@haddock.ISC.COM.UUCP (Karl Heuer) writes:
>>(This has to be done anyway when figuring out how to store the stuff.  For
>>example, if you push a byte on the 68000 stack a whole word is used.)
>
>I don't think this is relevant.  If the architecture requires (or the compiler
>prefers) the stack pointer to always be aligned, then push(byte) may have to
>be compiled into movb(byte, *--sp) and clrb(*--sp), but here the adjustment is
>by a constant amount known at compile-time.

On the 68000 ( and PDP-11 ), (sp)+ and -(sp) add or subtract 2 to/from the
sp for byte operands. The 'clrb' method would be non-atomic. So the 'fix'
is done in microcode.
 Whether the odd byte is cleared on a write is a different matter.
It isn't on the PDP-11; movb ...,-(sp) must be followed by clrb -1(sp)
to do that. I seem to recall that a 68000 does a 1-byte write, too,
but you can't then change it to a word by clearing the high byte since
the byte write occurs into the high byte ( confounded bigendians :-)).

As you say, this is irrelevant to the C pointer debate.

-- 
----------------------------------------------------------------------
Greg Smith     University of Toronto      UUCP: ..utzoo!utcsri!greg
Have vAX, will hack...

gwyn@brl-smoke.ARPA (Doug Gwyn ) (12/15/86)

In article <264@bobkat.UUCP> m5d@bobkat.UUCP (Mr Mike McNally) writes:
>I think that further suggestions along the lines of "let's change the
>language so I can do something disallowed for good reason in the
>existing language" should be directed to the ANSI committee, where they
>hopefully will be directed to the ANSI wastebasket.

Well, if you send an official comment on the proposed draft standard
to ANSI, they will eventually send it to the X3J11 committee, who have
to evaluate the comment and provide a response. It would be to
everybody's advantage if only well thought-out comments (including
persuasive arguments and specific wording for any proposed changes)
are submitted.  Note that a common reason for rejecting suggestions
is, approximately: "new invention; conflicts with existing practice;
need not convincingly shown; accomplishable with existing facilities".

Changes are, however, made when appropriate.  Here is an *unofficial*
list of changes approved at last week's X3J11 meeting:

several editorial changes, mostly for clarification

exit() status argument for success is 0 or EXIT_SUCCESS; for failure,
EXIT_FAILURE; other status values have implementation-defined meaning

DBL_DIG and LDBL_DIG minimum values to be increased, probably to 10

portable guaranteed subset for #include names to be provided

section 4.5.6.4 Description: delete 2nd sentence; Returns: add: if y
is 0, fmod returns 0.0.

permit stripping of trailing blanks from text input lines

name of strcoll() changed to strxfrm(); new routine just like X/OPEN
nl_strcmp() now called strcoll() (this can cause confusion unless one
specifies "new strcoll()" or "old strcoll() as in published draft")

malloc(0) meaning is now implementation-defined (a majority wanted
it to allocate a distinct pointer, but it takes a 2/3 majority to
make changes to the draft now)

I post these in the hope that if you were thinking about recommending
one of these changes, you will now be spared the effort of writing up
the comment.

mishkin@apollo.uucp (Nathaniel Mishkin) (12/15/86)

Apologies for what must have seemed an inexplicable query about
incrementing after a cast, considering the topic was already being
discussed.  The topic seemed so obscure, and I don't read "comp.lang.c",
so I thought it was safe to simply "catchup" before posting my query.

            -- Nat Mishkin
               Apollo Computer Inc.

P.S. I'll take BLISS -- it doesn't even have the concept of "lvalue".
     I never have gotten used to not having to type "."s all the time!

throopw@dg_rtp.UUCP (Wayne Throop) (12/16/86)

> braner@batcomputer.tn.cornell.edu (Moshe Braner)

> - When you cast a pointer to a pointer of another type, you are telling
> the compiler to use it differently (e.g. to read 2 instead of 1 byte
> from memory when dereferencing).  Since the compiler is aware of the cast
> in that sense, it COULD increment it according to the new type's size!
> I see no TECHNICAL obstacle here, only "legal" morass...

You are wrong, you know.  First, when you cast a pointer to a pointer of
another type, you are telling the compiler to CONVERT the pointer to a
pointer to that other type.  The bitwise value might be completely
different.  This is most common on word-addressed machines.  You are NOT
telling the compiler to treat the bits of that pointer as another type,
NOT telling it to "use [the value] differently".  You are telling it to
CHANGE THE VALUE to a new representation.

And thus, second, this forms a well-known technical obstacle to treating
casts of pointers (or casts of anything else, for that matter) as an
lvalue.  This is no legal quibbling.  It just can't be made to work on
some machines upon which C compilers are desirable.  Further, casts have
always been, are now, and (I surely hope) always will be CONVERSIONS.
Conversions, by their very nature, produce NEW VALUES.  I wouldn't
expect ((-x) = 10) to work, I wouldn't expect ((x+1) = 10) to work, I
wouldn't expect (((float)i) = 1.0) to work, and I wouldn't expect
(((char *)intptr) = &charvar) to work either, and all for the same
reason.

Can someone explain why anybody who has thought about it for more
than a few minutes WOULD expect that last example, or things like
((sometype *)p)++, to work?

--
Like punning, programming is a play on words.
                                --- Alan J. Perlis
-- 
Wayne Throop      <the-known-world>!mcnc!rti-sel!dg_rtp!throopw

ccplumb@watnot.UUCP (12/16/86)

In article <755@dg_rtp.UUCP> throopw@dg_rtp.UUCP (Wayne Throop) writes:
>
>Can someone explain why anybody who has thought about it for more
>than a few minutes WOULD expect that last example, or things like
>((sometype *)p)++, to work?

  I think the idea is it should act like...

temp = (sometype)p
p = (p'stype *)(temp++)

...which is what you could expect on a system where the type coercion
is a null operation (the bit patterns of p and (sometype *)p are
identical), so the temporary variable that holds (sometype *)p, and
gets incremented by the ++, is p itself.

  Of course, if the compiler checks syntax more carefully, or is on a
system where the bit patterns of the two types are different, this
will bomb out instantly.

	-Colin Plumb (ccplumb@watnot.UUCP)

Zippy says:
But was he mature enough last night at the lesbian masquerade?

.csnet"@relay.cs.net> (12/25/86)

Casts are conversions? Oh? You'd never know it looking at the code that
out of my C compilers ... including Harbison's!  He just regards away.

The fact that there are machines which would like to have C compilers on 
which ((sometype *)p)++ doesn't work as a "regard as" only means that 
these machines aren't compatible with a lot of people's notion of C's model 
of computation. There's a whole lot more machines on which ((sometype *)p)++ 
makes perfect legal, technical, and useful sense and these happen to be C 
machines. Why lots of people with ordinary machines can't do something 
useful, intuitive, and natural because some solder-crazed EE somewhere might,
just might mind you, someday do something unnatural to his memory system is 
beyond me.  

If you insist on being strict, here's a definition of ((sometype *)p)++ 
which will work on any machine:

	"Think of the bits refered to as p as a (sometype *).
	 If don't have enough bits or you have too many bits 
	 do something reasonable.  Now increment the (sometype *)
	 you're thinking of.  Now put the results back into p.  
	 If you	 don't have enough bits or you have too many bits, 
	 do something reasonable."

And no noise please about doing something reasonable because 1) we do it 
all the time with numbers so why not with pointers too, 2) it's probably 
what you want any way, and 3) it'll never have to be done on machines that 
are reasonable to begin with. (By the way there is no reason why ((x+1) = 10)
shouldn't work too.)

I forsee a day when there are two kinds of C compilers: standard ones and 
useful ones ... just like Pascal and Fortran.  Are we making progress yet?

tps@sdchem.UUCP (Tom Stockfisch) (12/26/86)

In article <2029@brl-adm.ARPA> .csnet"@relay.cs.net> writes:
>Casts are conversions? Oh? You'd never know it looking at the code that
>out of my C compilers ... including Harbison's!  He just regards away.
>
>The fact that there are machines which would like to have C compilers on 
>which ((sometype *)p)++ doesn't work as a "regard as" only means that 
>these machines aren't compatible with a lot of people's notion of C's model 
>of computation.

Your notion that

	(sometype *)p

could be an lvalue is not compatable with the notion of C's model of
computation.  As has been mentioned, even if you allow the "++" to operate
on the "(sometype *)p", according to C's model of computation the cast
value would exist in a temporary, and the TEMPORARY would be incremented,
not p.

>makes perfect legal, technical, and useful sense and these happen to be C 
>machines. Why lots of people with ordinary machines can't do something 
>useful, intuitive, and natural because some solder-crazed EE somewhere might,
>just might mind you, someday do something unnatural to his memory system is 
>beyond me.  
>...
>If you insist on being strict, here's a definition of ((sometype *)p)++ 
>which will work on any machine:
>
>	"Think of the bits refered to as p as a (sometype *).
>	 If don't have enough bits or you have too many bits 
>	 do something reasonable.  Now increment the (sometype *)
>	 you're thinking of.  Now put the results back into p.  
>	 If you	 don't have enough bits or you have too many bits, 
>	 do something reasonable."
>

Someone posted a more precise definition that translates the construct
into currently legal C.  I believe it was something like

	((sometype *)p)++

is defined to mean

	(*(sometype **)&p)++

but now I forget if the value of this expression is of the desired type.
I think your definition assumes that pointers will differ only in width,
and not in represention.  Don't forget that on many machines the interpre-
tation of a bit pattern is different for character pointers than for other
types.

>And no noise please about doing something reasonable because 1) we do it 
>all the time with numbers so why not with pointers too, 2) it's probably 
>what you want any way, and 3) it'll never have to be done on machines that 
>are reasonable to begin with. (By the way there is no reason why ((x+1) = 10)
>shouldn't work too.)

This violates the C design principle that an expression can be an lvalue --
what do you do with
	
	(x + y) =	10

What rule would you propose for assignments?  Some rule like:

	expr-containing-a-single-lvalue =	expr

will reject the "(x+y) =" case, but then what do you do with

	f(x) =	expr

Can you think of a scheme which wouldn't double the size of C's grammar?
I think that any scheme that allows "(sometype *)p" to be an lvalue in some
contexts will severely complicate C's grammar.  We don't want a language
where the special-cases part of the definition is larger than the perfectly-
general-rules section.

>I forsee a day when there are two kinds of C compilers: standard ones and 
>useful ones ... just like Pascal and Fortran.  Are we making progress yet?

The usual complaint about C is that it's TOO useful.  That is, that almost
any combination of tokens yields a valid program (or so say its detractors),
so that the compiler rarely discovers errors.
Your proposal would certainly contribute to this,
since it is usually an error for a cast to be an lvalue.

Recall all the confusion resulting from
the fact that arrays are sometimes the whole array and sometimes
the pointer to the first element, and the declaration "char *p" is
sometimes equivalent to "char p[]", and sometimes completely different.
Do you really want cast-expressions to be sometimes lvalues and
sometimes not?

And the main (sole?) advantage of this extension is so you can shave
two characters off the expression

	(*(sometype **)&p)++

I rest my case.

|| Tom Stockfisch, UCSD Chemistry	tps%chem@sdcsvax.UCSD

guy%gorodish@Sun.COM (Guy Harris) (12/29/86)

From the original article:

> >Casts are conversions? Oh? You'd never know it looking at the code that
> >out of my C compilers ... including Harbison's!  He just regards away.

Oh, really?  The code generated for

	float f;
	int i, j;

	f = (float)i + (float)j;

just adds two integer variables without converting to float?  How
interesting....

If the compiler *can* convert pointers without doing any computation, it
will; however, that does not mean this is the *definition* of such a
conversion.

> >The fact that there are machines which would like to have C compilers on 
> >which ((sometype *)p)++ doesn't work as a "regard as" only means that 
> >these machines aren't compatible with a lot of people's notion of C's model 
> >of computation.

That's their problem; they just don't understand C's model of computation.

From the followup:

> And the main (sole?) advantage of this extension is so you can shave
> two characters off the expression
> 
> 	(*(sometype **)&p)++

Well, not really.  This wouldn't work if "p" were of storage class
"register".

What people seem to want to do with this sort of construct is to generate
the "right" machine code for what they're trying to do.  They want to
generate something using the machine's auto-increment addressing mode, if
the machine has one.  If they wanted to write more portable code, they could
do

	*(sometype *)p ...
	p += sizeof (sometype);

and some compiler might even be clever enough to generate the right code.

I guess either not enough compilers are clever enough, so they want to use
something like Mesa's "LOOPHOLE" operator, which merely reinterprets a
bucket of bits.  It's interesting to note that C lacks this operator, while
some languages with stronger type checking have it.

ron@brl-sem.ARPA (Ron Natalie <ron>) (12/30/86)

In article <2029@brl-adm.ARPA>, .csnet"@relay.cs.net> writes:
> If you insist on being strict, here's a definition of ((sometype *)p)++ 
> which will work on any machine:
> 
> 	"Think of the bits refered to as p as a (sometype *).
> 	 If don't have enough bits or you have too many bits 
> 	 do something reasonable.  Now increment the (sometype *)
> 	 you're thinking of.  Now put the results back into p.  
> 	 If you	 don't have enough bits or you have too many bits, 

Actually, that is not how cast works, so your definition is far
from "strict."  Consider instead the following:

	"Ignore casts when applied to lvalues."

Since
	( (type1 *) p)++

is defined to mean
	((type1 *) p) = ((type1 *) p) + 1

What gives the compiles heartburn is the cast to the lvalue, and if
you just ignore it, then you get what I think you are looking for.

However, this leads to problem #2.  What should the type of the expression
be?  type1 or the original type of p?  You can probably make arguments
either way, however in a "strict" definition, you had better decide which
it is.

-Ron

jsdy@hadron.UUCP (Joseph S. D. Yao) (01/02/87)

In article <2029@brl-adm.ARPA> .csnet"@relay.cs.net> writes:
(Something confused, including:)
>	"Think of the bits refered to as p as a (sometype *).
>	 If don't have enough bits or you have too many bits 
>	 do something reasonable.  Now increment the (sometype *)
>	 you're thinking of.  Now put the results back into p.  
>	 If you	 don't have enough bits or you have too many bits, 
>	 do something reasonable."

Others have already commented on special-casing, models of
computation, and the like, and I couldn't do it better.
Let me pick at this a moment.
First, "increment":  what's the sizeof(bits)?  By how much does
one increment?
Second: "something reasonable": I  w i l l  pick at this, despite
your request.  There is a good model (modulo arithmetic) for
what to do if different integers are different sizes.  What
model do you propose for pointers?  For  s t r u c t u r e s ?
C gives warnings on some casts, anyway; now you want to make
this even worse?
Third: what is wrong with (*sigh*):
	type1 p;
	p = (type1) ((type2)p + 1);

If anyone replies with "too many keystrokes" I am not responsible
for the responses he or she gets.  From anyone.  Self included.
-- 

	Joe Yao		hadron!jsdy@seismo.{CSS.GOV,ARPA,UUCP}
			jsdy@hadron.COM (not yet domainised)

throopw@dg_rtp.UUCP (Wayne Throop) (01/03/87)

> .csnet"@relay.cs.net>
> Casts are conversions? Oh? You'd never know it looking at the code that
> out of my C compilers ... including Harbison's!  He just regards away.

Interesting.  I suppose then you'd conclude that "int" and "long" must
be the same type in C, since code generated by many pcc based compilers
includes no conversions for casts from one to the other?  Gad, what are
they teaching in schools that belong to the CSnet these days, anyhow?

> The fact that there are machines which would like to have C compilers on 
> which ((sometype *)p)++ doesn't work as a "regard as" only means that 
> these machines aren't compatible with a lot of people's notion of C's model 
> of computation. There's a whole lot more machines on which ((sometype *)p)++ 
> makes perfect legal, technical, and useful sense and these happen to be C 
> machines. Why lots of people with ordinary machines can't do something 
> useful, intuitive, and natural because some solder-crazed EE somewhere might,
> just might mind you, someday do something unnatural to his memory system is 
> beyond me.  

Sigh.  Interesting tirade I suppose.  Totally incorrect, of course.  The
problem is not that some hardware or another can or can't do this or
that.  The problem is that, in C, a cast is a unary operator that
produces a new value, and many people are ignorant of this simple fact.
Dragging word-addressed machines into the argument is simply to remind
people of one of the many reasons WHY casts (casts of pointers in
particular) are defined as conversions and not "regard as", and have
nothing whatever to do with the point that they ARE, IN FACT so defined.

> And no noise please about doing something reasonable because 1) we do it 
> all the time with numbers so why not with pointers too,

What, you say things like ((short)i)++ or ((float)i)++ "all the time"?
How... interesting.

>  2) it's probably what you want any way, and

Oh, great.  Just what we need, more constructs that'll "probably" do
what we "want" done.  It's bad enough (in order to allow performance
tradeoffs) that C defines many primitive declarations to be ambiguous,
and I'll even go along with it and proclaim it a Fairly Good Idea.  But
really...  "probably what you want any way" indeed!  Is this any way to
run a language definition?  I doubt it.

> 3) it'll never have to be done on machines that 
> are reasonable to begin with. (By the way there is no reason why ((x+1) = 10)
> shouldn't work too.)

So... 80-mumble-86's are "unreasonable", right?  And PDP-10s, and Crays
(I think), and CDC machines, and many, many more.  "Unreasonable"
computers form a much larger segment of the world than one might think.

And just what will ((x+1) = 10) DO, anyhow?  Oh, I forgot, it'll
"probably do what I want", yeah, that's it.  And (1=2) means to double
all constants in the program at runtime, right?  I mean, it's a
reasonable interpretation, right?  "Probably" what I "wanted" right?
So the compiler should just shut up and do what I said, and not give me
an impertinent error message, right?  Sheesh.

--
The negative judgment is the peak of mentality.
                                --- Alfred North Whitehead
-- 
Wayne Throop      <the-known-world>!mcnc!rti-sel!dg_rtp!throopw

gwyn@brl-smoke.ARPA (Doug Gwyn ) (01/05/87)

In article <2029@brl-adm.ARPA> .csnet"@relay.cs.net> writes:
>Casts are conversions? Oh? You'd never know it looking at the code that
>out of my C compilers ... including Harbison's!  He just regards away.

The sender's name and some of his text must have been processed by
software written according to his notions of type casts!

>... Why lots of people with ordinary machines can't do something 
>useful, intuitive, and natural because some solder-crazed EE somewhere might,
>just might mind you, someday do something unnatural to his memory system is 
>beyond me.  

Funny, I never considered Dennis Ritchie a "solder-crazed EE".
He explained to this list not long ago that type casts definitely
are conversions, done "as if" via assignment to unnamed temporary
variables.  I don't know why this is so hard for some people to
understand, unless perhaps there are instructors out there who
are teaching falsehoods.

mat@mtx5a.UUCP (01/13/87)

> >Casts are conversions? Oh? You'd never know it looking at the code that
> >out of my C compilers ... including Harbison's!  He just regards away.
> 
> The sender's name and some of his text must have been processed by
> software written according to his notions of type casts!
> 
> >... Why lots of people with ordinary machines can't do something 
> >useful, intuitive, and natural because some solder-crazed EE somewhere might,
> >just might mind you, someday do something unnatural to his memory system is 
> >beyond me.  
> 
> Funny, I never considered Dennis Ritchie a "solder-crazed EE".
> He explained to this list not long ago that type casts definitely
> are conversions, done "as if" via assignment to unnamed temporary
> variables.  I don't know why this is so hard for some people to
> understand, unless perhaps there are instructors out there who
> are teaching falsehoods.

What gets generated on a given machine for a given input is of only marginal
relevance to the general case.  It is always sufficient for the compiler to
produce code which is equivalent to the ``correct'' code in every way that
matters in the given situation; if in that situation, the effect of a cast can
be produced with a null code sequence, it's fine for the compiler to do that
*in that circumstance*.
-- 

	from Mole End			Mark Terribile
		(scrape .. dig )	mtx5b!mat
					(Please mail to mtx5b!mat, NOT mtx5a!
						mat, or to mtx5a!mtx5b!mat)
					(mtx5b!mole-end!mat will also reach me)
    ,..      .,,       ,,,   ..,***_*.