Although the expression given in the declaration is generally the correct way to use the variable, the relation between pointers and arrays allows for other uses. Since an array name is just a pointer, we can actually use the expressions b[] and *c as well. (Using the alternate notation is often confusing but occasionally more clear.) For example, the sixth element of an array, declared by either of the two methods mentioned above, can be accessed in either of the two following methods:
For a lab, your testing programs might know that the input is restricted to 1000 digits, but your multiplier should not know this. Thus, you will need to allocate one-dimensional arrays of unpredictable size within these functions. To do this, use the system library function malloc which will give you a specified number of contiguous bytes of memory. You must first, in your declarations, tell the compiler the type of the return value of malloc with the declaration (along with your variable declarations):
Now, let's say you want an array of 10 integers. Let A be an integer pointer (declared int *A). To get the array, use the command:
The (int *) preceeding the function call is called a cast. This changes the type of the return of malloc (which is a character pointer) into an integer pointer so that it may be stored in A. Often casts only affect the compiler's internal representation of what a type is but occasionally they generate code to physically modify data, so you should almost always use them when mixing things of differing types.
Always make sure, after calling malloc, that you actually got the space you requested. Malloc will return a null pointer if it could not get you the space. A null pointer can be represented in your code as a zero, or as NULL if you include the standard I/O file <stdio.h>.
Finally, the function free is used to return space to the operating system which was allocated by malloc. You should give, as a parameter, to free exactly the pointer given to you by malloc. You cannot give back part of an allocation. Thus there should be exactly as many calls to free as to malloc, and they should involve exactly the same blocks of memory.
For example, if an array is declared as
You will probably not be using this kind of array, since you don't have constant bounds on the sizes of any of your arrays.
The second type of array is a pointer-vector structure, where each dimension is represented by a vector of pointers of objects of the next dimension, except the last dimension, which consists of arrays of data. This sounds like a mess, but it really isn't:
If a three-dimensional array is declared as
In short, "array" points to a pointer to a pointer to an integer, "*array" points to a pointer to an integer, "**array" points to an integer, and "***array" is an integer.
In this case, an access of the form array[i][j][k] results in an access of the form
Which means: Take a pointer to the main array, add i to offset to the pointer to the correct second dimension array and indirect to it. Now we have a pointer to one of the arrays of 20 pointers, and we add j to get the offset to the next dimension, and we do an indirection on that. We now have a pointer to an array of 30 integers, so we add k to get a pointer to the desired integer, do an indirection, and we have the integer.
Passing arrays of this type is simple, you declare the parameter the same way, as "int ***arrayname".
Now the fun begins: how to allocate memory for a pointer-vector array. We get memory with the function
As before, we will assume that the variable is defined as
First, we need an array of 10 int **'s, so we use the following:
Note: malloc is asked for 10 int **'s but its return is a pointer to them, so the result is an int ***.
Now that we have the 10 pointers, we can get the next level of pointers:
for ( i = 0 ; i < 10 ; ++i ) { array[i] = (int **) malloc( 20 * sizeof(int *) ); }And finally, we can fill in each of these pointers with an array of 30 integers:
for ( i = 0 ; i < 10 ; ++i ) { for ( j = 0 ; j < 20 ; ++j ) { array[i][j] = (int *) malloc( 30 * sizeof(int) ); } }Again, remember that each call to malloc must check the result. Also note that we could have put the two steps above together, filling each set of 20 pointers as we get them.
It is much more efficient to combine all similar allocations and divide up the memory after getting it. (It is also much easier to make mistakes.) When you've convinced yourself the the following works, you will understand C pointers fairly well.
array = (int ***) malloc( 10 * sizeof(int **) ); array[0] = (int **) malloc( 10 * 20 * sizeof( int *) ); array[0][0] = (int *) malloc( 10 * 20 * 30 * sizeof(int) ); for ( j = 1 ; j < 20 ; ++j ) { array[0][j] = array[0][j-1] + 30; } for ( i = 1 ; i < 10 ; ++i ) { array[i] = array[i-1] + 20; array[i][0] = array[i-1][20-1] + 30; for ( j = 1 ; j < 20 ; ++j ) { array[i][j] = array[i][j-1] + 30; } }
This is the method you should use, but you will probably only need two dimensional arrays, which are far easier once you understand this example.
Space is returned to the system with the command
For returning the space to the system, you always return exactly what you were given, i.e., the exact pointer that malloc gave you. You cannot return a portion of an allocation. Thus there should be a perfect one-to-one correspondence between calls to malloc and calls to free.