home *** CD-ROM | disk | FTP | other *** search
/ BURKS 2 / BURKS_AUG97.ISO / BURKS / LANGUAGE / C / PTRTUT / CH9X.HTM < prev    next >
Text File  |  1997-07-19  |  16KB  |  523 lines

  1. <H2>CHAPTER 9: Pointers and Dynamic Allocation of Memory</H2>
  2.  
  3.  
  4.  
  5.     There are times when it is convenient to allocate memory at
  6. run time using <B>malloc()</B>, <B>calloc()</B>, or other allocation functions.
  7. Using this approach permits postponing the decision on the size
  8. of the memory block need to store an array, for example, until
  9. run time.  Or it permits using a section of memory for the
  10. storage of an array of integers at one point in time, and then
  11. when that memory is no longer needed it can be freed up for other
  12. uses, such as the storage of an array of structures.
  13.  
  14. <P>
  15.  
  16.     When memory is allocated, the allocating function (such as
  17. <B>malloc()</B>, <B>calloc()</B>, etc.) returns a pointer.  The type of this
  18. pointer depends on whether you are using an older K&R compiler or
  19. the newer ANSI type compiler.  With the older compiler the type
  20. of the returned pointer is <B>char</B>, with the ANSI compiler it is
  21. <B>void</B>.
  22.  
  23. <P>
  24.  
  25.     If you are using an older compiler, and you want to allocate
  26. memory for an array  of integers you will have to cast the char
  27. pointer returned to an integer pointer.  For example, to allocate
  28. space for 10 integers we might write:
  29.  
  30. <PRE>
  31.     int *iptr;
  32.     iptr = (int *)malloc(10 * sizeof(int));
  33.     if (iptr == NULL)
  34.  
  35.     { .. ERROR ROUTINE GOES HERE .. }
  36. </PRE>
  37.  
  38.     If you are using an ANSI compliant compiler, <B>malloc()</B> returns
  39. a <B>void</B> pointer and since a void pointer can be assigned to a
  40. pointer variable of any object type, the <B>(int *)</B> cast shown above
  41. is not needed.  The array dimension can be determined at run time
  42. and is not needed at compile time.  That is, the <B>10</B> above could
  43. be a variable read in from a data file or keyboard, or calculated
  44. based on some need, at run time.
  45.  
  46. <P>
  47.  
  48.     Because of the equivalence between array and pointer
  49. notation, once <B>iptr</B> has been assigned as above, one can use the
  50. array notation.  For example, one could write:
  51.  
  52. <PRE>
  53.     int k;
  54.     for (k = 0; k < 10; k++)
  55.        iptr[k] = 2;
  56. </PRE>
  57.  
  58. to set the values of all elements to 2.
  59.  
  60. <P>
  61.  
  62.     Even with a reasonably good understanding of pointers and
  63. arrays, one place the newcomer to C is likely to stumble at first
  64. is in the dynamic allocation of multi-dimensional arrays.  In
  65. general, we would like to be able to access elements of such
  66. arrays using array notation, not pointer notation, wherever
  67. possible. Depending on the application we may or may not know
  68. both dimensions at compile time.  This leads to a variety of ways
  69. to go about our task.
  70.  
  71. <P>
  72.  
  73.     As we have seen, when dynamically allocating a one
  74. dimensional array its dimension can be determined at run time.
  75. Now, when using dynamic allocation of higher order arrays, we
  76. never need to know the first dimension at compile time.  Whether
  77. we need to know the higher dimensions depends on how we go about
  78. writing the code.  Here I will discuss various methods of
  79. dynamically allocating room for 2 dimensional arrays of integers.
  80.  
  81. <P>
  82.  
  83.     First we will consider cases where the 2nd dimension is known
  84. at compile time.
  85.  
  86. <H3>METHOD 1:</H3>
  87.  
  88.     One way of dealing with the problem is through the use of the
  89. <B>typedef</B> keyword.  To allocate a 2 dimensional array of integers
  90. recall that the following two notations result in the same object
  91. code being generated:
  92.  
  93. <PRE>
  94.  
  95.     multi[row][col] = 1;     *(*(multi + row) + col) = 1;
  96.  
  97. </PRE>
  98.  
  99.     It is also true that the following two notations generate the
  100. same code:
  101.  
  102. <PRE>
  103.  
  104.     multi[row]            *(multi + row)
  105.  
  106. </PRE>
  107.  
  108.     Since the one on the right must evaluate to a pointer, the
  109. array notation on the left must also evaluate to a pointer.  In
  110. fact <B>multi[0]</B> will return a pointer to the first integer in the
  111. first row, <B>multi[1]</B> a pointer to the first integer of the second
  112. row, etc.  Actually, <B>multi[n]</B> evaluates to a pointer to that
  113. array of integers that make up the n-th row of our 2
  114. dimensional array. That is, <B>multi</B> can be thought of as an array
  115. of arrays and <B>multi[n]</B> as a pointer to the n-th array of this
  116. array of arrays.  Here the word <B>pointer</B> is being used
  117. to represent an address value.  While such usage is common in the
  118. literature, when reading such statements one must be careful to
  119. distinguish between the constant address of an array and a
  120. variable pointer which is a data object in itself.
  121.  
  122. <P>
  123.  
  124. Consider now:
  125.  
  126. <PRE>
  127.  
  128. --------------- Program 9.1 --------------------------------
  129.  
  130. /* Program 9.1 from PTRTUT10.HTM  6/13/97 */
  131.  
  132. #include <stdio.h>
  133. #include <stdlib.h>
  134.  
  135. #define COLS 5
  136.  
  137. typedef int RowArray[COLS];
  138. RowArray *rptr;
  139.  
  140. int main(void)
  141. {
  142.     int nrows = 10;
  143.     int row, col;
  144.     rptr = malloc(nrows * COLS * sizeof(int));
  145.     for (row = 0; row < nrows; row++)
  146.     {
  147.         for (col = 0; col < COLS; col++)
  148.         {
  149.             rptr[row][col] = 17;
  150.         }
  151.     }
  152.  
  153.     return 0;
  154. }
  155. ------------- End of Prog. 9.1 --------------------------------
  156.  
  157. </PRE>
  158.  
  159.     Here I have assumed an ANSI compiler so a cast on the void
  160. pointer returned by <B>malloc()</B> is not required.  If you are using
  161. an older K&R compiler you will have to cast using:
  162.  
  163. <PRE>
  164.     rptr = (RowArray *)malloc(.... etc.
  165. </PRE>
  166.  
  167.     Using this approach,<B> rptr</B> has all the characteristics of an
  168. array name name, (except that rptr is modifiable),  and array notation may be used throughout the rest of
  169. the program.  That also means that if you intend to write a
  170. function to modify the array contents, you must use COLS as a
  171. part of the formal parameter in that function, just as we did
  172. when discussing the passing of two dimensional arrays to a
  173. function.
  174.  
  175.  
  176.  
  177. <H3>METHOD 2:</H3>
  178.  
  179.     In the METHOD 1 above, rptr turned out to be a pointer to
  180. type "one dimensional array of COLS integers".  It turns out that
  181. there is syntax which can be used for this type without the need
  182. of <B>typedef</B>.  If we write:
  183.  
  184. <PRE>
  185.  
  186.     int (*xptr)[COLS];
  187.  
  188. </PRE>
  189.  
  190. the variable <B>xptr</B> will have all the same characteristics as the
  191. variable <B>rptr</B> in METHOD 1 above, and we need not use the
  192. <B>typedef</B> keyword.  Here <B>xptr</B> is a pointer to an array of
  193. integers and the size of that array is given by the <B>#defined
  194. COLS</B>.  The parenthesis placement makes the pointer notation
  195. predominate, even though the array notation has higher
  196. precedence.  i.e. had we written
  197.  
  198. <PRE>
  199.     int *xptr[COLS];
  200. </PRE>
  201.  
  202. we would have defined <B>xptr</B> as an array of pointers holding the
  203. number of pointers equal to that #defined by COLS.  That is not
  204. the same thing at all.  However, arrays of pointers have their
  205. use in the dynamic allocation of two dimensional arrays, as will
  206. be seen in the next 2 methods.
  207.  
  208. <H3>METHOD 3:</H3>
  209.  
  210.    Consider the case where we do not know the number of elements
  211. in each row at compile time, i.e. both the number of rows and
  212. number of columns must be determined at run time.  One way of
  213. doing this would be to create an array of pointers to type<B> int</B>
  214. and then allocate space for each row and point these pointers at
  215. each row.  Consider:
  216.  
  217. <PRE>
  218.  
  219. -------------- Program 9.2 ------------------------------------
  220.  
  221. /* Program 9.2 from PTRTUT10.HTM   6/13/97 */
  222.  
  223. #include <stdio.h>
  224. #include <stdlib.h>
  225.  
  226. int main(void)
  227. {
  228.     int nrows = 5;     /* Both nrows and ncols could be evaluated */
  229.     int ncols = 10;    /* or read in at run time */
  230.     int row;
  231.     int **rowptr;
  232.     rowptr = malloc(nrows * sizeof(int *));
  233.     if (rowptr == NULL)
  234.     {
  235.         puts("\nFailure to allocate room for row pointers.\n");
  236.         exit(0);
  237.     }
  238.  
  239.     printf("\n\n\nIndex   Pointer(hex)   Pointer(dec)   Diff.(dec)");
  240.  
  241.     for (row = 0; row < nrows; row++)
  242.     {
  243.         rowptr[row] = malloc(ncols * sizeof(int));
  244.         if (rowptr[row] == NULL)
  245.         {
  246.             printf("\nFailure to allocate for row[%d]\n",row);
  247.             exit(0);
  248.         }
  249.         printf("\n%d         %p         %d", row, rowptr[row], rowptr