throopw@dg_rtp.UUCP (Wayne Throop) (10/21/86)
> NET-RELAY.ARPA>@brl-smoke.ARPA (Scott) > I'm relatively new to info-c and don't know if you've had this debate > so just banish me to the archive if you have. It has come around before. > C pedants claim that casting destroys lvalueness. Their argument is > essentially that they can imagine a machine on which casting forces > the use of a temp so lvalueness is gone. Not that they can imagine such a machine. That such machines actually exist. I am editing on one now. > C users, on the other hand, find they have to program real machines not > hypothetical ones and that almost all of these real machines don't use a > temp when casting. For example, a useful and readable way to move a pointer > through a buffer containing a mixture of objects of different sizes is > > ((OBJECT)pointer)++ > > This construct is disallowed by Harbison's compiler. Not really very readible. Let's take a similar example. Would you expect int i; ((short)i)++; to do anything sensible? If so, why? If not, why should the pointer case work sensibly? After all, there *are* machines upon which integers and shorts "mean the same thing", so the cast and increment could work sometimes, right? > I fear that the C standards committee is going to take away such practical > constructs and turn production quality C compilers into academic quality > ones. Who knows, there may develop a brisk business in C compilers that > promise NOT to be standard conforming. Only among those who don't care about portability or maintainability. > How sayeth the C standard committee? How sayeth the users? I sayeth that if thou wishest to taketh an object as a different typeth, thou mayest do so. However, casts are not the way to do this in C, and the practice is not portable. If you must take the bits of one pointer type as being those of another pointer type, use (*((some_type **)&p))++ or use unions like God intended. Don't try to pervert casts to do something they weren't intended for. Casts convert, unions take-as. The take-as operation is inherently non-portable. -- "Thank you for your support." -- Wayne Throop <the-known-world>!mcnc!rti-sel!dg_rtp!throopw
NET-RELAY.ARPA>@brl-smoke.ARPA (10/21/86)
I'm relatively new to info-c and don't know if you've had this debate so just banish me to the archive if you have. C pedants claim that casting destroys lvalueness. Their argument is essentially that they can imagine a machine on which casting forces the use of a temp so lvalueness is gone. C users, on the other hand, find they have to program real machines not hypothetical ones and that almost all of these real machines don't use a temp when casting. For example, a useful and readable way to move a pointer through a buffer containing a mixture of objects of different sizes is ((OBJECT)pointer)++ This construct is disallowed by Harbison's compiler. I fear that the C standards committee is going to take away such practical constructs and turn production quality C compilers into academic quality ones. Who knows, there may develop a brisk business in C compilers that promise NOT to be standard conforming. How sayeth the C standard committee? How sayeth the users? Regards, Scott "You can hack any formalism so why not have useful formalisms?"
stuart@BMS-AT.UUCP (Stuart D. Gathman) (10/21/86)
In article <4617@brl-smoke.ARPA>, NET-RELAY.ARPA>@brl-smoke.ARPA writes: > For example, a useful and readable way to move a pointer > through a buffer containing a mixture of objects of different sizes is > ((OBJECT)pointer)++ A more correct syntax: { char *pointer; /* . . . */ pointer += sizeof (OBJECT); /* . . . */ } And clearer to boot if you ask me. -- Stuart D. Gathman <..!seismo!{vrdxhq|dgis}!BMS-AT!stuart>
desj@brahms (David desJardins) (10/23/86)
In article <4617@brl-smoke.ARPA>, NET-RELAY.ARPA>@brl-smoke.ARPA writes: > For example, a useful and readable way to move a pointer > through a buffer containing a mixture of objects of different sizes is > > ((OBJECT)pointer)++ In article <252@BMS-AT.UUCP> stuart@BMS-AT.UUCP (Stuart D. Gathman) writes: >A more correct syntax: > >{ > char *pointer; > /* . . . */ > pointer += sizeof (OBJECT); > /* . . . */ >} > >And clearer to boot if you ask me. Wrong if you ask me. First, in the original example it is clear that OBJECT is a pointer type. And second, your sample code does not work, and can not readily be fixed, if sizeof (char) != 1. In article <657@dg_rtp.UUCP> throopw@dg_rtp.UUCP (Wayne Throop) writes: >I sayeth that if thou wishest to taketh an object as a different typeth, >thou mayest do so. However, casts are not the way to do this in C, and >the practice is not portable. If you must take the bits of one pointer >type as being those of another pointer type, use > > (*((some_type **)&p))++ In C, pointers and lvalues are the same thing (there is a bijection given by & and *). Essentially, = (and the other assignment operators) automatic- ally take the address of their left-hand arguments, in much the same way that setq/set! quote their first arguments. In both cases this is done simply to enhance readability and reduce mistakes; x = x+1 is clearer and less error- prone than &x <- x+1 would be [store x+1 in the location &x], just as (setq x (+ x 1)) is clearer and less error-prone than (set (quote x) (+ x 1)). Without this syntactic sugar, the situation would be clearer. The C statement (foo) x = ... can correspond either to &((foo) x) <- ... or to (foo *) (&x) <- ..., depending on when the implicit address calculation takes place. Since only the latter makes any real sense, I submit that casting of lvalues should be interpreted in this way. So I would conclude that the statement ((OBJECT) pointer)++ should be interpreted as * ((OBJECT *) &pointer) = (OBJECT) pointer + 1; It is not absolutely clear that this is equivalent to Wayne Throop's alternative formulation (* ((OBJECT *) &pointer))++, which would give * ((OBJECT *) &pointer) = * ((OBJECT *) &pointer) + 1; or to the standard C construction for this type of operation, which is pointer = (char *) ((OBJECT) pointer + 1); But in any implementation in which casting of pointers works at all, I think that these should almost certainly give the same result. At any rate they are all legal C. >or use unions like God intended. Don't try to pervert casts to do >something they weren't intended for. Casts convert, unions take-as. >The take-as operation is inherently non-portable. But the point is that casts of pointers *don't* convert. Either they simply take-as, or they are meaningless. So, if the language allows casting of pointers, then I see no valid reason to complain when the programmer uses this feature (especially since essentially all C implementations make it impossible to avoid when using any sort of dynamic memory allocation!). And if he is allowed to use casting, why force him to write *((foo *) &x) = when (foo) x = will do? At any rate, I think that the answer to the question "*Must* casting destroy lvalues?" is clearly "No." -- David desJardins
stephen@datacube.UUCP (10/23/86)
I generally use or define caddr_t as the type of a generic pointer, and then use macros to perform the indicated operations, i.e.: caddr_t generic_pointer; #define DATA( p, type ) (*((type *)(p))) #define SUCC( p, type ) ((p) += sizeof(type)) #define PRED( p, type ) ((p) -= sizeof(type)) a = DATA(p,int); SUCC(a, int); b = DATA(p,double); SUCC(p, double); In the case of a compiler where the cast in DATA is invalid, an alternate formulation can be made. Stephen Watkins UUCP: ihnp4!datacube!stephen Datacube Inc.; 4 Dearborn Rd.; Peabody, Ma. 01960; 617-535-6644
desj@brahms (David desJardins) (10/24/86)
In article <657@dg_rtp.UUCP> throopw@dg_rtp.UUCP (Wayne Throop) writes: >Let's take a similar example. Would you expect > > int i; > ((short)i)++; > >to do anything sensible? If so, why? In my opinion this should have the result * ((short *) &i) = (short) i + 1; Obviously the result of this operation is machine-dependent (since the effect of casting int to short is machine-dependent). But on an appropriate machine this does indeed have not only a sensible but a *useful* effect -- it will increment the low bytes of i without carrying into the high bytes. At any rate the casting of int to short performs a fundamentally different operation than does casting of pointers (in Wayne's terminology, the former "converts" and the latter "takes-as"), and so it is not necessary for one to make sense in order for the other to be allowed. Note also that on most machines the statements int *p; ((int) p)++; makes sense (it increments the address referenced by p by the addressing unit of the machine). In fact this is arguably the correct way to use the value produced by 'sizeof'; (int) p += sizeof (foo); makes sense on any machine where 'sizeof' gives results in multiples of the addressing unit of the machine, whereas the more common alternative p = (int *) ((char *) p + sizeof (foo)); is both clumsier and makes the unnecessary assumption that sizeof (char) == 1. Another justification for casting of lvalues is the case of register variables. In this case Wayne's alternative syntax doesn't work: register int *p; (* ((foo **) &p))++; <== ERROR But the idea of casting (or "taking-as") the pointer p as a pointer to foo is still perfectly valid, and the proposed syntax ((foo *) p)++; still makes sense, and can be understood and implemented by a compiler. -- David desJardins
blarson@usc-oberon.UUCP (Bob Larson) (10/25/86)
In article <55@cartan.Berkeley.EDU> desj@brahms (David desJardins) writes: [some stuff so wrong that I decided to reply] >In article <4617@brl-smoke.ARPA>, NET-RELAY.ARPA>@brl-smoke.ARPA writes: >> For example, a useful and readable way to move a pointer >> through a buffer containing a mixture of objects of different sizes is >> >> ((OBJECT)pointer)++ Here is a major misconseption, based on a limited set of machines and a limited imagination: > But the point is that casts of pointers *don't* convert. Repeat until remembered: CASTS DO ANY NESSISARY CONVERSION. There are machines with more than one type of pointer. C makes no assuptions that require there only to be one type of pointer. "Byte" pointers on a PDP-10 include both the bit position and size of the object being pointed to. There are quite a few machines with different "word" and "character" pointers. To do what the original poster wanted (as portably as possible): (pointer = (type_of_pointer) ((char *)pointer + sizeof(OBJECT))) It says what you mean. It is no more ugly than what you are trying to do. The explicit casts are obvious places to look for portablility problems. [ If casts did not implicitly lose the lvalueness of the expression, can you tell me what the following code fragment should do? Please explain in terms the begining C programer who meant f += (float)i; could understand, as well as the non-portable C coder who means *(int *)&f = i; float f = 0.1; int i = 2; (int)f += i; ] -- Bob Larson Arpa: Blarson@Usc-Eclb.Arpa or blarson@usc-oberon.arpa Uucp: (ihnp4,hplabs,tektronix)!sdcrdcf!usc-oberon!blarson
throopw@dg_rtp.UUCP (Wayne Throop) (10/28/86)
David is very misinformed on this question. It is apparently an easy thing to do, since mis- and dis- information about casts and pointers in C is so common. Never fear, I'll point out, case by case, where he went wrong. :-) > desj@brahms (David desJardins) >> throopw@dg_rtp.UUCP (Wayne Throop) >>> Stuart Gathman >>>> ((OBJECT)pointer)++ >>>A more correct syntax: >>> char *pointer; >>> pointer += sizeof (OBJECT); >>>And clearer to boot if you ask me. > Wrong if you ask me. First, in the original example it is clear that > OBJECT is a pointer type. And second, your sample code does not work, and > can not readily be fixed, if sizeof (char) != 1. True, true. But ANSI is likely to decide that sizeof(char) MUST ALWAYS BE one (and I think this is universally true on existing implementations... if I'm wrong, somebody let me know). The relevant incantation from the draft standard (3.3.3.4): The sizeof operator yields the size (in bytes) of its operand, which may be an expression of the parenthesized name of a type. [...] When aplied to an operand that has type char, unsigned char, or signed char, the result is 1. In order to make Stuart's method work (since OBJECT is a pointer type, as David correctly points out) one must (oddly enough) say: pointer += sizeof( *((OBJECT)0) ); Now, in his critique of my earlier posting, David falls into *really* serious error, as follows: >>I sayeth that if thou wishest to taketh an object as a different typeth, >>thou mayest do so. However, casts are not the way to do this in C, and >>the practice is not portable. If you must take the bits of one pointer >>type as being those of another pointer type, use >> (*((some_type **)&p))++ > In C, pointers and lvalues are the same thing (there is a bijection given > by & and *). False. The "&" operator does not work on bit fields nor register values, and yet these are lvalues. In fact, close reading of K&R, H&S, and the draft ANSI standard make it clear that not all legal lvalues map to legal pointer typed expressions (via &), nor do all legal pointer typed expressions map to legal lvalues (via *). > Essentially, = (and the other assignment operators) automatic- > ally take the address of their left-hand arguments, in much the same way that > setq/set! quote their first arguments. Again, false, and for about the same reasons. The notion of an assignment operator implicitly quoting the assignee is nice, somewhat elegant, and a familiar notion in LISP. But don't be fooled, folks. C is not now and has never been LISP, and C does not have this simple, elegant, unifying notion. C has "lvalues" instead. > [expansion of this misunderstanding of lvalue, omitted] > So I would conclude that the statement ((OBJECT) pointer)++ should be > interpreted as > * ((OBJECT *) &pointer) = (OBJECT) pointer + 1; And you would be wrong. > [more discussion based on the misunderstanding of lvalue, omitted] > At any rate they are all legal C. It is important to note that the original construct, ((OBJECT)pointer)++ is *DEFINITELY* *NOT* legal C. (It is not clear to me whether David is claiming that it IS legal... in any case it is important to note that it is NOT.) >> [...] use unions like God intended. Don't try to pervert casts to do >>something they weren't intended for. Casts convert, unions take-as. >>The take-as operation is inherently non-portable. > But the point is that casts of pointers *don't* convert. Either they > simply take-as, or they are meaningless. Absolutely wrong. Casts *ALWAYS* convert. Harbison and Steele say (on 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 draft ANSI document says (3.3.4): Preceeding an expression by a parenthesized type name converts the value of the expression to the named type. K&R say similar things in several places. (I'll let y'all look those up by y'selfs.) Another problem in the above passage is that David seems to have a serious case of "pointers is pointers" disease. Pointers to different types may (and often do) have *COMPLETELY* *DIFFERENT* bit-wise formats. Thus, the notion of converting an (int *) typed pointer to a (char *) typed pointer is hardly "meaningless". On the DG MV architecture (to randomly choose an example I'm modestly familiar with) these two pointer types have the "ring field" in different locations, and one has an indirect bit that the other lacks. A pointer to a given word needs to be *CONVERTED* *TO* *A* *DIFFERENT* *FORMAT* to be a pointer to the first byte in that word. This is just what casts were intended for (first and foremost in the arithmetic types, but clearly useful and necessary for pointers also). David, I'm not sure who told you "casts of pointers don't convert". Find whoever told you that base canard, and pummel some sense into the miscreant, willya? You have been severely misled. > So, if the language allows casting > of pointers, then I see no valid reason to complain when the programmer uses > this feature (especially since essentially all C implementations make it > impossible to avoid when using any sort of dynamic memory allocation!). Yes, casting is necessary to write a memory allocator in any even nearly portable way. *BUT*, casts are *STILL* conversions, *NOT*, *NOT*, *NOT* taken-ases. > And if he is allowed to use casting, why force him to write *((foo *) &x) = > when (foo) x = will do? Granting the implicit hypothetical, no reason, of course. But (foo)x *won't* do. > At any rate, I think that the answer to the question > "*Must* casting destroy lvalues?" is clearly "No." Well, waxing philosophical, I agree. That is, I rather expect that one can come up with some consistent set of semantics that will give meaning to the notion of a cast as an lvalue. But it is well to keep in mind: (deep breath, all together now) these semantics won't be *C* semantics. -- "Pwease Mistew Game Wawden, ... can you teww me what season it WEAWWY is?" "Why SOIT'NY, m'boy! It's BASEBALL season!" --- (Elmer and Bugs, of course) -- Wayne Throop <the-known-world>!mcnc!rti-sel!dg_rtp!throopw
chris@umcp-cs.UUCP (Chris Torek) (10/29/86)
>In article <657@dg_rtp.UUCP> throopw@dg_rtp.UUCP (Wayne Throop) writes: >> (*((some_type **)&p))++ In article <55@cartan.Berkeley.EDU> desj@brahms (David desJardins) writes: >... the point is that casts of pointers *don't* convert. Yes they do, and Wayne Throop has to know it: His machine does indeed convert. `char *' is 48 bits; `int *' is 32 bits. DG machines use word pointers, except when dealing with bytes. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690) UUCP: seismo!umcp-cs!chris CSNet: chris@umcp-cs ARPA: chris@mimsy.umd.edu
gnu@hoptoad.uucp (John Gilmore) (10/29/86)
One interesting thing for people who don't understand this issue to do is to ask yourself what the compiler should do for a construct like: int i = 17; ((double)i)++; Again, don't post your favorite answer to the net. Just think about it. PS: When you're done that one, try: double i = 17; ((int)i)++; -- John Gilmore {sun,ptsfa,lll-crg,ihnp4}!hoptoad!gnu jgilmore@lll-crg.arpa Overheard at a funeral: "I know this may be an awkward time, but do you recall him ever mentioning source code?" -- Charles Addams
desj@brahms (David desJardins) (10/30/86)
In article <4038@umcp-cs.UUCP> chris@umcp-cs.UUCP (Chris Torek) writes: >In article <55@cartan.Berkeley.EDU> desj@brahms (David desJardins) writes: >>... the point is that casts of pointers *don't* convert. > >Yes they do, and Wayne Throop has to know it: His machine does >indeed convert. `char *' is 48 bits; `int *' is 32 bits. DG >machines use word pointers, except when dealing with bytes. My apologies; I misspoke. What I meant to say is that casts of pointers refer to the same physical locations, although the representations of pointers of different types may be different. -- David desJardins
john@uw-nsr.UUCP (John Sambrook) (10/31/86)
In article <144@cartan.Berkeley.EDU> desj@brahms (David desJardins) writes: >In article <4038@umcp-cs.UUCP> chris@umcp-cs.UUCP (Chris Torek) writes: >>In article <55@cartan.Berkeley.EDU> desj@brahms (David desJardins) writes: >>>... the point is that casts of pointers *don't* convert. >> >>Yes they do, and Wayne Throop has to know it: His machine does >>indeed convert. `char *' is 48 bits; `int *' is 32 bits. DG >>machines use word pointers, except when dealing with bytes. > > My apologies; I misspoke. What I meant to say is that casts of pointers >refer to the same physical locations, although the representations of pointers >of different types may be different. > > -- David desJardins Just a small correction. On the MV series both types of pointers are 32 bits. It is also true that character pointers have a different format than pointers to other types. Casting from one type to the other on the MV series requires a conversion. -- John Sambrook Work: (206) 545-7433 University of Washington WD-12 Home: (206) 487-0180 Seattle, Washington 98195 UUCP: uw-beaver!uw-nsr!john
throopw@dg_rtp.UUCP (Wayne Throop) (11/04/86)
> desj@brahms (David desJardins) >> throopw@dg_rtp.UUCP (Wayne Throop) >>Let's take a similar example. Would you expect >> int i; >> ((short)i)++; >>to do anything sensible? If so, why? > In my opinion this should have the result > * ((short *) &i) = (short) i + 1; "Opinion." OK. Fine. But David's opinion clearly and trivially differs from that of the folks who designed and implemented the C language. In particular, David's interpretation of casts has them sometimes converting, and sometimes taking-as. K&R, H&S, and the draft X3J11 standard are all as clear as they can be... casts *ALWAYS* convert. > Obviously the result of this operation is machine-dependent (since the effect > of casting int to short is machine-dependent). Casting is *NOT* machine dependent in anywhere near the same sense that taking the bits of an integer as if they were a short is. Again, David has a fundamental misunderstanding of what it is that a cast does. It *ALWAYS* converts. Casting an int to a short is machine dependant in the limit, but in the case where the two types share range, the result is dependable and machine independant. The draft X3J11 standard even outlines some guarantees on what this range of portable casting is. > But on an appropriate machine > this does indeed have not only a sensible but a *useful* effect -- it will > increment the low bytes of i without carrying into the high bytes. Oh, please! Just because some illegal construction can be made to do something useful on some machine-or-other is no reason to attempt to legislate it so. Especially when this operation can be specified with legal C constructs. > At any rate the casting of int to short performs a fundamentally different > operation than does casting of pointers (in Wayne's terminology, the former > "converts" and the latter "takes-as"), and so it is not necessary for one to > make sense in order for the other to be allowed. False, false, false! Pointers have differing bit-wise formats, just as arithmetic types do. The common examples are the word-addressed machines, where the byte or character address format differs from the "natural" architectural pointer format. > In fact this is arguably the correct way to use the value > produced by 'sizeof'; > (int) p += sizeof (foo); One can argue it. And anybody that did so would be wrong. A pointer taken-as an integer need not address in sizeof-unit-sized chunks. In fact, there are many machines where this is not the case. Further, there are machines where pointers aren't even the same *SIZE* as integers. Surely these two trivial, well-known facts should point out some flaws in any such argument? > Another justification for casting of lvalues is the case of register > variables. In this case Wayne's alternative syntax doesn't work: > register int *p; > (* ((foo **) &p))++; <== ERROR > But the idea of casting (or "taking-as") the pointer p as a pointer to foo is > still perfectly valid, and the proposed syntax > ((foo *) p)++; > still makes sense, and can be understood and implemented by a compiler. Rubbish. David just argued in a previous note that casts retaining lvalueness was justified because lvalues and addresses are "the same thing". Now he claims that register values further support this ludicrous position, despite the fact that they are a counterexample to his previous justification!! Puhleeeze! I'm trying hard to be civil here, honest I am, but really! If somebody wants to act like a C guru, it might help to learn some C first. Get this straight: casts convert. Unions take-as. A cast of a pointer can take-as the bits it points to, BUT NOT THE POINTER ITSELF! The take-as operation is completely machine dependant, and code using it is rendered non-portable by this use. This is trivial, basic stuff folks. You can't even *BEGIN* to understand C until you get *THIS* straight. -- "Don't do this at home, kids..." -- Wayne Throop <the-known-world>!mcnc!rti-sel!dg_rtp!throopw
leichter@yale.UUCP (Jerry Leichter) (11/05/86)
All this has really gotten out of hand. I've found I've wanted to use casts as lvalues in exactly one situation: Incrementing a pointer by an amount that is NOT to be taken as a multiple of the size of the type pointed to. A "common" - NONE of these are what I'd really want to call common, but I HAVE run into them more than once - case is in a (machine dependent!) storage allocator that wants to make sure of proper alignment. This doesn't arise in something like malloc, which wants a char * (void *, in ANSI C) anyway, but in specialized applications. For example, I need to allocate stuff out of a large array. The "stuff" will be of a known type - a struct whose last element is a varying-length array - and will thus be varying in size. The begining of anything allocated must be on an even address. So I have a pointer to some big structure that I'd like to increment by 1. Not 1*size of the structure, but 1. YES, THIS IS MACHINE DEPENDENT - I got tied down by such a dependency when I cast the pointer to int to look at the bottom bit! I can think of no particular use for casting of arbitrary lvalues, but in situations as above, the following definition for a cast argument to op= would be handy: (type)a op= b shall mean: a = (type)a op b (except that a is only evaluated once). Pre- and post-decrement and increment should work in the obvious way. Note that the type of (type)a op= b (and of ++(type)a, etc.) is the type of a, NOT the type being cast to. I can think of no real uses for this construct where "op" is anything but "+" or "-". -- Jerry
lambert@mcvax.uucp (Lambert Meertens) (11/06/86)
I still don't get what is so fundamentally wrong with the following *addition* to C, which it seems to me that desj@brahms (David desJardins) is arguing for: A cast (T)E in a context where an lvalue is required stands for *(T *)&(E) and so is lawful if the latter would be allowed here. For example, this would then make the following lawful: int i; char *p = (char *)&i; ++(int)*p; At least, in `my' cc (BSD 4.3) everything works as expected if I #define Lcast(T,E) (*(T*)&(E)) and then use ++Lcast(int, *p); (I don't know if this is allowed by the ANSI C draft.) Note that I am not particularly arguing in favour of this extension, the fact being that this is one of the areas where I can live with current C. But neither do I see a reason to get upset about it. It allows you to write pretty meaningless things, but that is the case already for current casts. One thing that is certain is that it does not invalidate existing code. Also, it seems consistent to me with the design philosophy of C: start by putting some very confusing things in (pointers vs. arrays, here pointers vs. lvalues), then legitimate some of the most obvious unambiguous mistakes. -- Lambert Meertens, CWI, Amsterdam; lambert@mcvax.UUCP