home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1993 #3 / NN_1993_3.iso / spool / comp / lang / c / 20009 < prev    next >
Encoding:
Internet Message Format  |  1993-01-21  |  8.2 KB

  1. Path: sparky!uunet!spool.mu.edu!agate!dog.ee.lbl.gov!horse.ee.lbl.gov!torek
  2. From: torek@horse.ee.lbl.gov (Chris Torek)
  3. Newsgroups: comp.lang.c
  4. Subject: 2-d arrays, pointers, etc, for the N'th time: with pictures
  5. Date: 21 Jan 1993 10:30:53 GMT
  6. Organization: Lawrence Berkeley Laboratory, Berkeley CA
  7. Lines: 206
  8. Message-ID: <28538@dog.ee.lbl.gov>
  9. References: <Dean_Wagner.49sy@aldhfn.akron.oh.us>
  10. NNTP-Posting-Host: 128.3.112.15
  11.  
  12. (This was a mail reply, but then I figured I might as well post it too.
  13. This version has some additional commentary.)
  14.  
  15. >     Given a 2-dimensional array, how is it possible to dynamically
  16. >allocate memory for the first dimension?
  17.  
  18. It is not possible; but you have asked the wrong question.  You
  19. should instead ask:
  20.  
  21.     Given the desire to obtain something that functions as if
  22.     it were a two-dimensional array, ...
  23.  
  24. and this question has (at least) two answers, both in the FAQ.
  25.  
  26. >In other words, I wanted to changed the declaration from
  27. >record[56][5] to
  28. >*record[5] or record[][5]
  29.  
  30. Neither of these is quite right.  If record is supposed to be accessed
  31. as if it were an `array M of array N of data_t', it must have one of
  32. the following three types:
  33.  
  34.     - array M of array N of data_t;
  35.     - pointer to array N of data_t;
  36.     - pointer to pointer to data_t.
  37.  
  38. The first case is simply, e.g.,
  39.  
  40.     data_t record[56][5];        /* M=56, N=5 */
  41.  
  42. The second form is:
  43.  
  44.     data_t (*record)[5];        /* pointer to array N=5 of ... */
  45.  
  46. and the third form is:
  47.  
  48.     data_t **record;
  49.  
  50. The latter two forms work because of The Rule (this is THE rule about
  51. arrays, which must simply be memorized):
  52.  
  53.     In any value context, an object of type `array N of T', for any
  54.     integer constant N and valid type T, becomes a value of type
  55.     `pointer to T', pointing to the first element of that array, i.e.,
  56.     the one with subscript 0.
  57.  
  58. Everything else about arrays and pointers is a consequence of The Rule,
  59. along with the rules of pointer arithmetic.  Pointer arithmetic assumes
  60. that objects are laid out contiguously in memory; since arrays *are*,
  61. The Rule works out perfectly.
  62.  
  63. Thus, if you declare, e.g.,
  64.  
  65.     int a[4][7];
  66.  
  67. you have created an object named `a' of type `array 4 of array 7 of
  68. int', or, if you prefer, `array of 4 arrays of 7 ints each'.  This has
  69. the form `array N of T': N=4 and T=`array 7 of int'.  (Array types are
  70. legal object types, of course, so T may have the form `array N2 of
  71. T2'.)  Placing the object `a' in a value context invokes The Rule.
  72.  
  73.     <value context> a ==> a is an object of type array N=4 of T=...
  74.  
  75. so we get a `pointer to T':
  76.  
  77.     <value, pointer to T=array 7 of int, &a[0]>
  78.  
  79. In C, the type `pointer to array 7 of int' is written `int (*)[7]'.
  80. (Note that the constant N `drops out'; this means that in many cases it
  81. can be ignored, and in some it need not even be supplied.  These are
  82. nonetheless special cases.  It *always* works if you supply the
  83. constant N; it *sometimes* works if you omit it.)
  84.  
  85. Now suppose instead of the *object* `a', we just figure out some
  86. suitable *value* and save it away somewhere.  This value should
  87. have the same type we just came up with---`int (*)[7]'---and we
  88. can save it in an object of that type.  So:
  89.  
  90.     int (*ap)[7];    /* declare ap as pointer to array 7 of int */
  91.  
  92.     ap = a;        /* `a' is in a value context here */
  93.  
  94. ap now points to the first element of a (a[0]), which is a single
  95. `array 7 of int'.  Note that ap points to all 7 ints, all at once.  If
  96. we now ask for *ap, we fetch this object---but it has type `array 7 of
  97. int', so if we put *that* in a value context, it again falls under The
  98. Rule.  This gives us a pointer to the first of those 7 ints, i.e.,
  99. &a[0][0], or &ap[0].
  100.  
  101. We can do exactly the same with the `record' variable: instead of
  102. making it an array object (array M=56 of T=...) we make it a pointer
  103. to `T'.  Now we can allocate space with malloc or calloc as needed,
  104. so that we are not stuck with exactly 56 objects of type T.
  105.  
  106. Note that if we use `data_t (*record)[5]', while we are not stuck
  107. with 56 arrays, we *are* stuck with 5 elements in each sub-array.  If
  108. this is not OK, then we will be forced to use the third form.  The
  109. third form has a disadvantage: it needs intermediate space for the
  110. second set of pointers, as illustrated below.
  111.  
  112. In the following pictures, space that typically comes from malloc() is
  113. not labelled at all---which seems fitting, since it does not have a
  114. specific name.  There is no requirement to use malloc/calloc, but if
  115. it is not used, there is little reason to bother with the pointers.
  116.  
  117. ====================================================================
  118.  
  119. With data_t record[56][5]:
  120.  
  121.     record
  122.     +---+---+---+---+---+
  123.     | 0 | 1 | 2 | 3 | 4 |   (record[0])
  124.     +---+---+---+---+---+
  125.     | 0 | 1 | 2 | 3 | 4 |   (record[1])
  126.     +---+---+---+---+---+
  127.           .
  128.           .
  129.           .
  130.     +---+---+---+---+---+
  131.     | 0 | 1 | 2 | 3 | 4 |   (record[55])
  132.     +---+---+---+---+---+
  133.  
  134.     (each record[i] is physically adjacent to record[i+1])
  135.  
  136. In this case `record' is a label for a block of 56*5 data_t objects.
  137. All of the objects are `right there': all you have to do to find them
  138. is go to the right row, then go to the right column.  Note that
  139. `record' is the entire block of 56*5 objects: sizeof(record) = 56*5*
  140. sizeof(data_t).
  141.  
  142. ====================================================================
  143.  
  144. With data_t (*record)[5]:
  145.  
  146.     record
  147.     +---+        +---+---+---+---+---+
  148.     | ------------> | 0 | 1 | 2 | 3 | 4 | sub-block 0
  149.     +---+        +---+---+---+---+---+
  150.             | 0 | 1 | 2 | 3 | 4 | sub-block 1
  151.             +---+---+---+---+---+
  152.                   .
  153.                   .
  154.                   .
  155.             +---+---+---+---+---+
  156.             | 0 | 1 | 2 | 3 | 4 | sub-block 55 (or whatever)
  157.             +---+---+---+---+---+
  158.         (each of these sub-blocks is physically adjacent)
  159.  
  160. In this case `record' is just a pointer.  The memory holding the
  161. various data objects must be allocated separately.  All of the objects
  162. are `over yonder': to find them, you read the contents of the variable
  163. `record', go there, then go to the right row, then to the right
  164. column.  Note that record[i] is an entire sub-block, or 5 objects:
  165. sizeof(record[i]) = 5*sizeof(data_t).  These sub-blocks are C arrays.
  166. Their contiguous arrangement---where each sub-block is followed by the
  167. corresponding next sub-block---makes the whole malloc()ed block `act
  168. like' an array, but because it lacks a fixed compile-time size, it is
  169. not actually a C array.
  170.  
  171. ====================================================================
  172.  
  173. With data_t **record:
  174.  
  175.     record
  176.     +---+        +---+      +---+---+---+---+---+
  177.     | ------------> | ------> | 0 | 1 | 2 | 3 | 4 |
  178.     +---+        +---+      +---+---+---+---+---+
  179.             | ------\
  180.     +---+---+---+---+    +---+    |    +---+---+---+---+---+---+
  181.     | 0 | 1 | 2 | 3 | <---- |   \-----> | 0 | 1 | 2 | 3 | 4 | 5 |
  182.     +---+---+---+---+    +---+        +---+---+---+---+---+---+
  183.                   .
  184.               .
  185.               .
  186.             +---+          +---+---+---+---+---+
  187.             | ----------> | 0 | 1 | 2 | 3 | 4 |
  188.             +---+          +---+---+---+---+---+
  189.  
  190. In this case `record' is again just a pointer, but this time it does
  191. not point to the first of a whole series of data objects.  Instead, it
  192. points to the first of a whole series of pointers.  The objects
  193. themselves are `over the hills and through the woods'.  First we have
  194. to read the value of `record' to find the series of pointers.  Next we
  195. select the row---one of the series of pointers---and read its value to
  196. find the series of columns.  Note that record[i][j] is a single
  197. object: sizeof(record[i][j]) = sizeof(data_t).  There are no actual
  198. arrays here, merely things that `act like them' due to contiguous
  199. allocation.
  200.  
  201. In this case, the set of 56 (or whatever number of) intermediate
  202. pointers must be contiguous, but each little group of 5 can be anywhere
  203. independent of any other group of 5.  Since each group of 5 is
  204. independent, they need not all be groups of 5: some can have 4, or 9,
  205. or whatever, as shown above.  Of course, you can *make* them all
  206. adjacent by allocating all 56*5 (or however many) at once, and then set
  207. up the intermediate pointers as needed.
  208.  
  209. The last arrangement is clearly much more flexible.  There is a price,
  210. however: the intermediate pointers take some storage.  They also take
  211. extra time to load, which may or may not be offset by the time spent
  212. calculating the proper row and column in the other versions.  The time
  213. difference is negligible in most code; the space and `shape' difference
  214. is typically the most important.
  215. -- 
  216. In-Real-Life: Chris Torek, Lawrence Berkeley Lab CSE/EE (+1 510 486 5427)
  217. Berkeley, CA        Domain:    torek@ee.lbl.gov
  218.