Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.1 6/24/83 (MC840302); site mcvax.UUCP Path: utzoo!watmath!clyde!burl!ulysses!allegra!mit-eddie!godot!harvard!seismo!mcvax!guido From: guido@mcvax.UUCP (Guido van Rossum) Newsgroups: net.lang.c Subject: Re: limitations of casts, pointer and function declarartions... Message-ID: <6126@mcvax.UUCP> Date: Tue, 30-Oct-84 08:19:02 EST Article-I.D.: mcvax.6126 Posted: Tue Oct 30 08:19:02 1984 Date-Received: Wed, 31-Oct-84 01:19:40 EST References: <120@harvard.ARPA> Reply-To: guido@mcvax.UUCP (Guido van Rossum) Organization: "Stamp Out BASIC" Committee, CWI, Amsterdam Lines: 78 Keywords: casts, typedefs, lvalues, rvalues, recursive types, C, Algol-68 In article <120@harvard.ARPA> breuel@harvard.ARPA (Thomas M. Breuel) writes: > int x; > char *y; >/*### [cc] illegal lhs of assignment operator = %%%*/ > (char *)x = y; Sorry, you're thinking Algol-68. What you need is: *( (char*) &x ) = y; Let me try to explain why. The difference between lvalues and rvalues in C is quite different from the difference between 'int' and 'ref' 'int' in Algol-68. Here are the rules: The following are declared to be lvalues: - names of variables (except arrays, see elsewhere in net.lang.c :-); - the expression *something, where 'something' may be any expression; - e1[e2]; this can be deduced from the previous rule because, by definition, e1[e2] means *((e1)+(e2)) /* remember special semantics of pointer+int */; - an lvalue between parentheses is still an lvalue. Everything else is an rvalue. (Summary: lvalues have addresses; rvalues don't. But lvalues *are* not addresses. They're variables.) In expressions (note that assignments are also expressions), there is a need for lvalues and rvalues. An lvalue is needed: - at the left-hand side of an assignment operator (hence the name); - as an argument to the auto-increment/decrement operators ++ and -- (note that the RESULT of these is only an rvalue!); - as an argument to the address-of operator, &something. Everywhere else, an rvalue is needed. When an lvalue is found where an rvalue is needed, it is turned into an rvalue by using the value contained in its address. Again, summarizing: after int x;, x and 1 have exactly the same type; only x is an lvalue and 1 is an rvalue. >(the solution here is, of >course, to write 'x = (int)y;', but can the compiler make this >transformation without ambiguity in general?). Huh? This assigns to all (sizeof int) bytes of x, while * ( (char*) &x ) = y; assigns only to x's first (or last) byte. What did you want? >typedef ref *ref; Looks very much like Algol-68 again (except that there, you can never use the thing at all, because there are no unrestricted casts as in C...). The fact is, and this will not change, that a typedef cannot contain references to itself (the typedef-ed name becomes defined only *after* the typedef has been processed by the compiler). The only way to build recursive types is using structure pointers, as it *is* allowed to write struct foo *x; /* but not struct foo x; !!! */ when struct foo is not yet declared (see response by Doug Gwyn). >typedef long base; /* change this to int type with size of pointer */ >typedef base *ref; >#define deref(thing) ((ref)(*thing)) How about this: #define deref(thing) (* (ref*)(thing)) Because the '*' operator is at the outermost level, this macro expands to an lvalue, with the same type as your macro, and can thus be used in an assignment. >typedef fun (*fun)(); Same remarks: typedefs can't be recursive. Sorry, that's the way it is. -- Guido van Rossum, "Stamp Out BASIC" Committee, CWI, Amsterdam guido@mcvax.UUCP "Don't stop. Go right on complaining. It's *so* beautiful!"