Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!watmath!clyde!burl!ulysses!allegra!mit-eddie!think!harvard!seismo!rlgvax!hadron!jsdy From: jsdy@hadron.UUCP (Joseph S. D. Yao) Newsgroups: net.lang.c Subject: Re: Address of array Message-ID: <322@hadron.UUCP> Date: Sat, 22-Mar-86 03:49:50 EST Article-I.D.: hadron.322 Posted: Sat Mar 22 03:49:50 1986 Date-Received: Tue, 25-Mar-86 03:23:08 EST References: <428@batcomputer.TN.CORNELL.EDU> Reply-To: jsdy@hadron.UUCP (Joseph S. D. Yao) Organization: Hadron, Inc., Fairfax, VA Lines: 131 Summary: int (*x)[5]; -- it exists! But garry's code's wrong. In article <428@batcomputer.TN.CORNELL.EDU> garry%geology@cu-arpa.cornell.edu.arpa writes: >In a recent article jsdy@hadron.UUCP (Joseph S. D. Yao) wrote: >>... There is no such thing as a pointer to the whole >>array: that is a Pasqualische or Fortranian notion. Pointers, in >>C, only point to atomic or aggregate (structure/union) objects... > >Are you sure of this? I sometimes write: > foo (ap) register float (*ap)[4][4]; {... (*ap)[0][0] = 33; ...} >I do this because my compiler (DEC/Vms) ends up making slightly better >use of registers than if I wrote: > foo (array) float array[4][4]; {... array[0][0] = 33; ...} >(and because it's slightly more pleasing to my brain actually to say "pointer- >to-array" if that's what I'm thinking of). Are you saying my syntax is legal >only by the grace of DEC? > >garry wiegand >garry%cadif-oak@cu-arpa.cs.cornell.edu garry @ cadif-oak or geology: You bring up a good point, and one that I hadn't thought to mention. You are in fact using a pointer to a matrix! Your compiler probably makes different code because you don't say register float array[4][4]; in the second one. (Try it!) I don't understand why your compiler and lint don't complain about your code, though. These two pieces of code are in fact not equivalent in their declarations, though they are the same in effect, as I will explain below. By the way, there are (lots of!) known problems with VMS "C", in terms of what it does versus what all other "C" compilers do. DEC does document most of these; I am not at all sure how thouroughly. The good point is this. Whenever you pass the address of an array, of a n y d i m e n s i o n , as an argument to a function, it comes out on the other side as a pointer. As I mentioned in the last article, this pointer can be looked at as pointing to an object of atomic type, no matter how deeply dimensioned the original array, because all the array is, is a set of objects next to each other in memory. There are no pointers or complex aggregate objects in that memory. This is one way (the machine's way) of looking at it. Another way might be to consider each level of dimensioning an aggregate: so, an array of int [7] is seven ints in a row; and a matrix (2-d array) of int [5][7] is an array of 5 (array of 7 ints in a row)s in a row. Then x[3][4] is the 3*7+4th or 25th int in the row. And then, x is that elusively-sought object, the address of [i.e., a pointer to] an array of 7 ints. BUT, at the same time it is the address of the first int, and the address of an array of 35 ints, and the address of a 5 X 7 matrix of ints! How do we tell the difference? At this point, it might be worth sitting down and drawing a picture of the array, both in X-Y form: x[0][0], x[0][1], ... , x[0][6], x[1][0], ... x[1][6], ... x[4][0], ... x[4][6] and in memory-address order: _x: x[0][0] _x+4: x[0][1] ... _x+136: x[4][6]. Now, obviously, if I coerce x to be an int array, I can also index it from 0 to 34. As mentioned above, whenever one uses an array name as an argument to a function, it gets coerced into a pointer, and must be declared correctly on the other side. For instance, int **x; would be WRONG here: why? ... ... Because, as we said before, there are no pointers here, as there would have to be to have a pointer to a list of pointers. Similarly, int *x[] is WRONG: this also declares a list of pointers, and is equivalent to int **x. The declarations int *x; and int x[]; are also equivalent to each other; and, while they may be used instead of the truth (indexed, as I said, 0 - 34), they are not the truth. Now, int x[5][7]; is the truth. This can, by the way, also be written as int x[][7]; without the compiler objecting, since the compiler does not need to know the first subscript. After all, you can have any number of 7-int arrays pointed to by the passed pointer. Since, as stated above, int *x; and int x[]; are the same, then can anyone see why int x[][7]; and int (*x)[7]; shouldn't be the same? I thought not ... In fact, they are the same. Much to my surprise, we have found the elusive pointer to an array! If I declare int (*x)[7]; I should be able to increment x and get it to point to the next row of seven ints. Well, hush my puppies, it does exactly that! (Cc on 4.2BSD on ISI 68000 Q-bus machine. Cc on Xenix 2.8b on 8086 Altos 986. Cc on Ultrix 1.1 on VAX 11/750. These are the closest machines I can get to on one hop. All agree.) And, in fact, this falls out logically from the language description -- it just is a little-used turn of phrase (as it were). So, what does x[3][4] mean in this context? It means index x by 3, to get the third further row of 7 ints from the one pointed to by x; then take the 4th further int in that row. Or, the 25th int, just as before! Now, why is garry's code wrong? Well, just as we can have a 1-d alias for a 2-d array, so can we have a 3-d alias. And that is exactly what garry has created for us. The declaration: int (*x)[5][7]; is the same as int x[][5][7]. The use of this, as (*x)[3][4], is the same as x[0][3][4]. How do we translate this? Well, x is a pointer to a number of sequential 5X7 matrices. In this sequence of matrices, we are selecting the 0*35+3*7+4th int, or the 25th int. Therefore, the effect is the same, even though we are saying that we are using a 3-d array! In fact, we can take this to as many dimensions as we want. Yes, Virginia, this is confusing. I'm sorry. As I explained last time, it has a little to do with the power of the language, and a lot to do with history. To change it would break a whole lot of existing C programs, which is one of the things that ANSI X3J11 is trying not to do (except for #endif label's, grr grr). And so would should tune our mental models accordingly and learn to live with it. By the way: I can understand not having lint to tell one that the declaration and call in the C program have distinctly different declarations. VMS has been trying to catch up with UNIX in terms of software tools, and seems to be falling rapidly behind. But surely, if you passed an &'d array to a function, a compiler error message something like: "tst.c", line 7: warning: & before array or function: ignored should have come out! Doesn't it? That should tell you something! Disclaimer: any misteaks in the past 130 or so lines are purely the result of my home workstation bottling up this message and squirting it out past midnight so that it looks and feels like I wrote it in my sleep ... The C did compile the way I described, though. No denying it. Pointers to arrays LIVE! -- Joe Yao hadron!jsdy@seismo.{CSS.GOV,ARPA,UUCP}