Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.2 9/18/84; site utcsri.UUCP Path: utzoo!utcsri!greg From: greg@utcsri.UUCP (Gregory Smith) Newsgroups: comp.lang.c Subject: Re: Auto variable with sizeof == 0 Message-ID: <4053@utcsri.UUCP> Date: Mon, 2-Feb-87 22:17:17 EST Article-I.D.: utcsri.4053 Posted: Mon Feb 2 22:17:17 1987 Date-Received: Wed, 4-Feb-87 05:40:51 EST References: <4114@brl-adm.ARPA> Reply-To: greg@utcsri.UUCP (Gregory Smith) Organization: CSRI, University of Toronto Lines: 58 Summary: In article <4114@brl-adm.ARPA> escott%deis.uci.edu@icsg.uci.edu writes: >Somebody recently came to me with a program that worked on a VAX 11/750 >running 4.2BSD but failed on our Sequent Balance 21000 running Dynix 2.1. > [...] after examining the code in question, I found a construct that >seems a little strange to me: an automatic variable was declared as a >"struct foo **bar[]". "How could this be right?" I said to myself. "How >can you declare an automatic variable that has no size?" > >So I wrote a program that contained a similar declaration, and then tried to >take sizeof( bar ). Sure enough: > warning: sizeof returns 0 >[This from the VAX 11/750 4.2BSD compiler] > >Okay, that makes sense. My question is: is there any reason why you should >be able to declare an array with zero elements as an automatic variable? >What's strange is that, on the VAX, the program apparently successfully >dereferenced bar, both setting a value for "*bar" and then using that value >later. How can this be right? How can "bar" have any value at all, much >less "*bar"? If there is no use for a zero-sized automatic variable, how >come the compiler lets you do it? Well, *bar is just bar[0], and is of type (struct foo **). There is indeed no storage reserved for this array element. It is like declaring 'char blat[6]' and then setting "blat[6]='?';". blat[0] through blat[5] exist, and blat[6] is simply the next char after blat[5]. The C language does not guarantee what that is. On the vax compiler the 'bar' declaration reserves zero words for the array 'bar', and then bar[0] is the word *after* that zero-length array. Despite having no length, the array still has an address, and bar[0] is effectively stored at this same address. Since the array occupies zero memory, another variable may start in the same place, and bar[0] will reference the memory occupied by this other variable. The goof who wrote it probably knew that, with the VAX compiler, setting *bar would actually set the next declared auto (a little knowledge is a dangerous thing). I.e. if it looks like this: { struct foo **bar[]; char *ptr; ... Then ptr and *bar are stored in the same place. Of course this is non-portable as you have found. Presumably the code using *bar depends on this shared storage. A quick and dirty fix (fight nasty with nasty?) is #define bar (struct foo ***)&ptr instead of the declaration for bar. This will achieve the same effect and is considerably more portable. It still isn't really correct; the shared storage should be done either by use of a union, or by casting between the stored type and the 'struct foo **' type. The choice depends on what is actually being done with this pointer. "No one ever said it was going to be easy...." -- ---------------------------------------------------------------------- Greg Smith University of Toronto UUCP: ..utzoo!utcsri!greg Have vAX, will hack...