The simplest aggregate data type is an array: a group of data items that share the same type and a common name. You can make an array from any data type, including basic types such as char and int and more complex types such as structures. This section shows how to declare, initialize, and access arrays, including arrays with more than one dimension. We'll begin with a simple example that creates a one-dimensional array.
The ARRAY.C program creates the array i_array, which contains three
integers.
/* ARRAY.C: Demonstrate one-dimensional array. */
#include <stdio.h>
main()
{
int j;
int i_array[3];
i_array[0] = 176;
i_array[1] = 4069;
i_array[2] = 303;
printf( "--- Values -------- --- Addresses -------\n\n" );
for( j = 0; j < 3; j = j + 1 )
{
printf( "i_array[%d] = %d", j, i_array[j] );
printf( "\t&i_array[%d] = %u\n", j, &i_array[j] );
}
}
Here is the output from ARRAY.C:
--- Values -------- --- Addresses -------
i_array[0] = 176 &i_array[0] = 3506
i_array[1] = 4069 &i_array[1] = 3508
i_array[2] = 303 &i_array[2] = 3510
As you can see, ARRAY.C prints the values in i_array and the memory address where each array element is stored. You usually don't have to worry about actual memory addresses in C, but it's useful to have some idea how array elements are stored in memory. Depending on factors such as the amount of memory in your system, you may see different addresses when you run ARRAY.C.
(The second printf statement uses the “address-of” operator (&) to determine the address of each array element. For more information on this operator, see Chapter 6. For now, it's sufficient to recognize that the operator allows ARRAY.C to print addresses.)
Figure 4.2 shows how i_array is stored in the addresses from the ARRAY.C output.
You declare an array variable by stating its type and its name, as you would a simple variable. You must also declare the size of the array, stating the number of elements with an integer constant in square brackets. For example, the line
int i_array[3];
from ARRAY.C declares a three-element integer (int) array named i_array.
Multidimensional arrays are declared the same way, except you must give the size of each dimension. The following statement, for instance, declares a two-dimensional int array named two_dim:
int two_dim[2][3];
We'll return to multidimensional arrays a little later in this chapter.
Arrays, like simple variables, should be initialized before use. ARRAY.C initializes i_array with these statements:
i_array[0] = 176;
i_array[1] = 4069;
i_array[2] = 303;
Summary: An array can be initialized when it is declared.
ARRAY.C declares an array in one statement and then initializes its elements one by one. You can also initialize an array when you declare it. The following statement does both jobs at once:
int i_array[3] = { 176, 4069, 303 };
Note the curly braces around the initializing values. The braces are mandatory in this kind of initialization.
Under the ANSI C standard, which QuickC for Windows follows, you can simultaneously declare and initialize an array within a function. Pre-ANSI compilers, including the DOS version of QuickC (version 1.0), don't allow this unless the static keyword precedes the array declaration. Chapter 5, “Advanced Data Types,” discusses static.
When you declare and initialize an array at the same time, the initializing values are normally constants, as shown above. Occasionally, you may want to initialize an array as you declare it using variables instead of constants. QuickC for
Windows allows this, but only within a function. The sample array in the following example is initialized legally under QuickC for Windows but illegally under the DOS version of QuickC:
func()
{
int val = 5;
int sample[3] = { val, val, val };
}
If you initialize a local array in this way, you must include the size of the array within the square brackets following the array name. If the example initialized the sample array with the following line:
int sample[ ] = {val, val, val};
QuickC would issue an error because the size of the array (3) is not specified.
Summary: Array subscripts are enclosed in square brackets ( [ ] ).
You specify an array element by giving its position, using an integer value called a “subscript.” Square brackets ([]) enclose each subscript. In the ARRAY.C program above we specify the first element of i_array as
i_array[0]
Notice that the first element of a C array has the subscript 0, not 1. Unlike QuickPascal and QuickBasic, the C language does not give you the option to start at an index number other than 0.
Since array subscripts begin at 0, the subscript of the last array element is 1 less than the number used to declare that dimension of the array. In ARRAY.C, the last element of i_array is i_array[2], not i_array[3].
Summary: C doesn't check array subscripts.
Unlike QuickBasic and QuickPascal, C doesn't check the validity of array subscripts. If the ARRAY.C program included the expression
i_array[55];
it would refer to a nonexistent array element. (The expression refers to the element 55, but i_array contains only three elements.) This would not trigger a compiler error or run-time error, however. It's your job to remember the size of the array and avoid references that go outside the array's boundaries. This rule is also important when you're accessing arrays with pointers. For more information, see Chapter 8.
Summary: A string is an array of characters.
You may have wondered why we didn't mention strings in our earlier description of basic data types. The reason is that strings aren't a formal data type. In the C language, a string is simply an array of characters (char values).
The STRING.C program below creates the string c_array and displays its contents in the same format as the previous example. The program prints the value of each array element and its address.
/* STRING.C: Demonstrate string arrays. */
#include <stdio.h>
main()
{
int j;
char c_array[] = "Hello";
printf( "--- Values -------- --- Addresses -------\n\n" );
for( j = 0; j < 6; j = j + 1 )
{
printf( "c_array[%d] = %x %c", j, c_array[j], c_array[j] );
printf( "\t&c_array[%d] = %u\n", j, &c_array[j] );
}
}
Here is the output from STRING.C:
--- Values -------- --- Addresses -------
c_array[0] = 48 H &c_array[0] = 3522
c_array[1] = 65 e &c_array[1] = 3523
c_array[2] = 6c l &c_array[2] = 3524
c_array[3] = 6c l &c_array[3] = 3525
c_array[4] = 6f o &c_array[4] = 3526
c_array[5] = 0 &c_array[5] = 3527
Figure 4.3 shows how c_array is stored in memory. Again, the addresses in the output may differ depending on factors such as the amount of available memory.
Summary: A string ends with a null character.
The figure illustrates another important feature of strings. Although c_array has five printing characters (Hello), it actually contains six characters—five letters plus a null character (\0) that marks the end of the string. As noted earlier, the C language automatically adds a null character to every string enclosed in double quotes.
STRING.C uses a shortcut when it initializes c_array. You may have noticed that the array declaration
char c_array[] = "Hello";
doesn't declare the array's size (the square brackets are empty). When an array is initialized at the same time it's declared, QuickC can figure out how many elements the array has by counting the number of initializing values to the right of the equal sign.
You can use this shortcut for any type of array, not just a char array. If the array has more than one dimension, however, you can only omit the size of the first dimension.
A “multidimensional” array contains two or more array dimensions. The TWODIM.C program below creates a two-dimensional array named i_array.
/* TWODIM.C: Demonstrate multidimensional arrays. */
#include <stdio.h>
main()
{
int j, k;
int i_array[2][3] = { { 176, 4069, 303 }, { 6, 55, 777 } };
printf( "--- Values -------- --- Addresses -------\n\n" );
for( j = 0; j < 2; j = j + 1 )
{
for( k = 0; k < 3; k = k + 1 )
{
printf( "i_array[%d][%d] = %d", j, k, i_array[j][k] );
printf( "\t&i_array[%d][%d] = %u\n", j, k, &i_array[j][k] );
}
printf( "\n" );
}
}
Here's the output from TWODIM.C:
--- Values -------- --- Addresses -------
i_array[0][0] = 176 &i_array[0][0] = 3498
i_array[0][1] = 4069 &i_array[0][1] = 3500
i_array[0][2] = 303 &i_array[0][2] = 3502
i_array[1][0] = 6 &i_array[1][0] = 3504
i_array[1][1] = 55 &i_array[1][1] = 3506
i_array[1][2] = 777 &i_array[1][2] = 3508
Each subscript of a multidimensional array appears in its own set of square brackets, as the TWODIM.C output shows. When you declare the array, the first subscript states the size of the first dimension, the second states the size of the second dimension, and so on. In TWODIM.C, the declaration of i_array,
int i_array[2][3]
states that i_array contains two rows of values, each row containing three integers. The statement that declares i_array also initializes the array, listing the initializing values in curly braces to the right of the equal sign:
int i_array[2][3] = { { 176, 4069, 303 }, { 6, 5, 77 } };
The braces clearly show that the array contains two groups of three values.
Two-dimensional arrays are often pictured in rows and columns, as in Figure 4.4. Of course, since computer memory is linear, i_array is actually stored with its rows end-for-end, as in Figure 4.5.
You refer to a multidimensional array element the same way you would a one-dimensional array element, except that you use one subscript for each dimension of the array. For instance, the statement
printf( "%d\n", i_array[0][1] );
specifies two subscripts. It prints the value stored in element 0, 1 of i_array, which is 4069.
Figure 4.5 shows how to specify every element of i_array in TWODIM.C.