Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.1 6/24/83 (MC830713); site edai.UUCP Path: utzoo!watmath!clyde!floyd!vax135!ukc!edcaad!edee!edai!ok From: ok@edai.UUCP (Richard O'Keefe) Newsgroups: net.lang.c Subject: Functions with union parameters Message-ID: <4107@edai.UUCP> Date: Tue, 10-Apr-84 16:41:36 EST Article-I.D.: edai.4107 Posted: Tue Apr 10 16:41:36 1984 Date-Received: Sat, 7-Apr-84 01:31:12 EST Organization: Art.Intelligence,Edin.Univ. Lines: 65 A recent message in this newsgroup cited a fragment of the SIII tty driver (I think that was it) as an example of a union without a struct around it. Thank you Guy Harris for something I can show people to say why DAI doesn't want SIII (:-)! The fragment went something like foo(..., baz, ...) ... union {ushort ch; somethingelse *pt} baz; {...} ushort c; somethingelse *p; foo(..., c, ...) foo(..., p, ...) Sorry folks, it's not portable. A difficulty on some machines that lack the VAX's nice address modes is that if you have big arrays in the stack, some of the local variables of a function can go beyond the range of base+displacement addressing. SO, on such machines compiler writers (legitimately!) put array bodies onto a second stack and leave pointers to them in the normal stack. So long as they hide the fact that there's actually a pointer variable there, nobody hurts. But records can get pretty big too (e.g. the 4.1bsduser structure is 4k bytes according to /usr/include/sys/param.h) and the same fix is applied. And compilers tend to treat unions as a special case of structs. So when a function has a struct or union (rather than a pointer to a struct or union) as a parameter, it may look for it on the same stack as scalars (as in the VAX C compilers), OR IT MAY LOOK FOR IT SOMEWHERE ELSE. A program which lint is happy with couldn't care less. A replacement which looks ok but isn't is typedef union {ushort ch; somethingelse *pt;} *hackp; foo(..., baz, ...) hackp baz; {... /* use *baz instead of baz */} ... foo(..., (hackp)&c, ...) foo(..., (hackp)&p, ...) Is there any guarantee that ch is at offset 0 in the union? If there is, fine, but if a compiler could align union fields on the *other* end, it's trouble time, as baz->ch might mean say ((ushort*)(baz))[1]. A clean method when there are just two alternatives like this is to always pass two parameters, one of them a dummy. E.g. foo(..., ch, pt, ...) ushort ch; somethingelse *pt; {...} ... foo(..., c, (somethingelse*)0, ...) foo(..., 0, p, ...) The overhead is probably negligible, and if there is an "impossible" value for one of the arguments (in this case pt == NULL is probably a good one) that can be the indication to use the other argument. Another hack you sometimes see, struct zz {int a,b;}; foo(x) struct zz x; {...} ... foo(1, 2) ... suffers from the same problem, only worse, as you have alignment to worry about even when all arguments go on the same stack. Struct and union arguments have their uses, but a Lint option for asking to be warned about them would be a Good Thing.