ANÁLISIS DE LAS OBRAS REPRESENTATIVAS DE SOLEDAD CÓRDOVA
3.4. ANÁLISIS DEL CUENTO INFANTIL: LEER EN LA CAMA
C provides rectangular multi-dimensional arrays, although in practice they are much less used than arrays of pointers [KR88, page 110].
A multi-dimensional array is defined using multiple adjacent square brackets, and the elements of the array may be initialised with values enclosed in curly braces, as in the following example.
float matrix[3][4] = { { 2.4, 8.7, 9.5, 2.3 }, { 6.2, 4.8, 5.1, 8.9 }, { 7.2, 1.6, 4.4, 3.6 } };
The braces of the initialisation list are nested such that the inner braces enclose each row of the two-dimensional array. In memory, a multi-two-dimensional array is laid out row-wise as a single two-dimensional array. For example, the layout in memory ofmatrixis equivalent to the following 1-D array.
float oneD[] = { 2.4, 8.7, 9.5, 2.3, 6.2, 4.8, 5.1, 8.9, 7.2, 1.6, 4.4, 3.6 };
As for one dimensional arrays, multi-dimensional arrays may be defined without a specific size.
However, only the left-most subscript (i.e., the number of rows) is free, and the other dimensions must be given a definite value. For example,
float matrix[][4] = { /* The 4 must be specified. */
{ 2.4, 8.7, 9.5, 2.3 }, { 6.2, 4.8, 5.1, 8.9 } };
To access an element of a multi-dimensional array, the correct notation is to enclose each subscript in a separate pair of square braces. This differs from many other programming languages, which use comma separated subscripts.
float a = matrix[1][2]; /* Correct. */
float b = matrix[1,2]; /* Wrong. */
A multi-dimensional array may have any number of dimensions, and higher-dimensional initialiser lists involve a deeper level of nested braces for each dimension. The following example illustrates the general format.
short array3d[4][2][3] = {
{ { 0, 1, 2 }, { 3, 4, 5 } }, { { 6, 7, 8 }, { 9, 10, 11 } }, { { 12, 13, 14 }, { 15, 16, 17 } }, { { 18, 19, 20 }, { 21, 22, 23 } } };
Note, the above array may have been defined with the left-most subscript unspecified, short array3d[][2][3] = ... ;
but the other dimensions must be fully qualified.
An example program using multi-dimensional arrays is given below. This program defines a two-dimensional array to represent a 3-by-3 matrix and passes it to a function which computes the product of two matrices.
1 #include<stdio.h>
2
3 #defineSIZE3 4
5 voidmultiply(int (*r)[SIZE],const inta[ ][SIZE],const intb[SIZE][SIZE]) 6 /* Multiply two (SIZE by SIZE) matrices, a and b, and store result in r.
7 * The result matrix, r, must be zeroed before-hand. */
8 {
9 int i,j,k; 10
11 for(i= 0;i<SIZE; ++i) 12 for(j= 0;j<SIZE; ++j) 13 for(k= 0;k<SIZE; ++k) 14 r[i][j] +=a[i][k] *b[k][j];
15 } 16
17 intmain(void) 18 {
19 int m1[ ][SIZE] ={
20 {1, 2, 3 },
21 {4, 5, 6 },
22 {7, 8, 9 }
23 };
24
25 int m2[SIZE][SIZE] ={0};
26 int i,j; 27
28 multiply(m2,m1,m1);
29 for(i=0;i<SIZE; ++i){ 30 for(j=0;j<SIZE; ++j) 31 printf("%3d ",m2[i][j]);
32 printf("\n");
33 }
34 }
The output for this program is the square of matrixm1.
30 36 42 66 81 96 102 126 150
Notice, on line 5, the function interface for multiply() shows three equivalent declarations:
a pointer to an array, a 2-D array with an unspecified left subscript, and a fully specified 2-D array, respectively. Notice that the pointer to an array is required to specify the size of the non-left-most subscript. In addition, notice the parentheses about the pointer-to-an-array identifier, int (*r)[SIZE], this is to distinguish it from an array-of-pointers,int *r[SIZE].
Often multi-dimensional arrays and arrays of pointers may be used in an identical fashion. This can be a source of confusion to novice C programmers. For example, given the following array definitions,
char a[] = { 1, 2, 3 };
char b[] = { 4, 5, 6 };
char *c[] = { a, b };
char d[][3] = { { 1, 2, 3 }, { 4, 5, 6 } };
The subscriptsc[i][j]andd[i][j]will give the same results.
However, multi-dimensional arrays and arrays of pointers are different in both their representation and application. First, a multi-dimensional array occupies a single contiguous region of memory, while an array-of-pointers may point to disparate memory locations. Second, a multi-dimensional array is rectangular—each row is the same length, while an array of pointers may refer to arrays of different length (including nothing,NULL). Finally, a multi-dimensional array requires all but the left-most subscript to be specified when it is passed to function, while an array of pointers makes no such requirement.
In summation, arrays of pointers are usually the more flexible (and often more efficient) option, and so are used far more frequently than multi-dimensional arrays.
Chapter 9
Dynamic Memory
Often a program cannot know in advance how much memory it will require. Setting aside “enough”
memory, such as defining achararray of sufficient size to hold the largest permitted string, is not convenient in many situations and may be difficult to manage.
Dynamic memory facilitates memory on demand. Memory is requested at runtime as required, and the lifetime of each memory allocation is controlled directly by the programmer.
9.1 Different Memory Areas in C
C has four distinct areas of memory: the constant data area, the static-extent data area, the stack, and the heap. The first of these stores string constants and other data whose values are known at compile time. This area of memory is read-only, and the results of trying to modify it are undefined.
The second data area is for variables that are definedexternorstatic, and so exist for the lifetime of the program. These variables are read-writable. Both of the first two memory areas are allocated when the program begins and destroyed when it terminates, and are managed by the compiler.
The memory area known as thestackis used to store local variables (i.e., variables with automatic extent). A local variable is allocated memory at the point where it is defined and this memory is released immediately as the variable goes out-of-scope. The stack behaves like a last-in-first-out (LIFO) queue. When variables are defined, they are “pushed onto the stack”, and the stack-size increases. And at the end of a block, if several variables go out-of-scope at once, the variables are destroyed, or “popped off the stack”, in reverse order to their allocation. Stack memory allocation is managed entirely by the compiler.
The heap memory area is for dynamically allocated storage and is managed, not by the com-piler, but explicitly by the programmer. Requests for memory, and its subsequent destruction, are performed via a set of standard library functions, and the programmer has complete control over the lifetime of an allocated memory block. The flexibility and control provided by heap-allocated memory comes at the price of added responsibility on behalf of the programmer. The compiler does not check that the memory is managed correctly, and dynamic memory errors are a plentiful source of subtle runtime bugs.
It is worth noting that stack memory allocation is generally much faster than heap memory allocation, as stack allocation involves only an increment of the stack pointer, while heap allocation involves much more complex operations. For this reason, stack allocation is often preferred over heap allocation even at the cost of some wasted storage space. For example, an over-sizechararray, allocated on the stack, is often used to perform string operations rather than an exact-size array created on demand on the heap.