Chapter 11: Working with Arrays

We have already seen how a structure is a grouping of one or more components that can each be of different data types. Often, we need a grouping that consists of the same type; this is called an array. An array is a collection of multiple occurrences of the same data type grouped together under a single name. Each element of the array is accessed via its base name and an offset of that base. Arrays have many uses, from organizing homogenous data types to providing the basis for strings, or arrays of characters.

Before we can learn about some of the wide uses of arrays, we need to explore the basics of declaring and manipulating arrays.

The following topics will be covered in this chapter:

  • Declaring an array of values
  • Initializing an array in several ways
  • Understanding variable-length arrays
  • Accessing each element of an array
  • Understanding zero-based array indexing
  • Assigning and manipulating elements of an array
  • Using looping statements to access all elements of an array
  • Using array references as function parameters

Technical requirements

Continue to use the tools chosen from the Technical requirements section of Chapter 1, Running Hello, World!.

The source code for this chapter can be found at https://github.com/PacktPublishing/Learn-C-Programming-Second-Edition/tree/main/Chapter11.

Declaring and initializing arrays

An array is a collection of two or more values, all of which have the same type and share a single common base name. It makes no sense to have an array of just one value; that would simply be a variable. An array definition has the following syntax: dataType arrayIdentifier[ numberOfElements ]; Here, dataType is any intrinsic or custom type, arrayIdentifier is the base name of the array, and numberOfElements specifies how many values of dataType are in the array. numberOfElements, for whatever type and values are given, is a literal constant or expression that will be converted to an integer. Most commonly, numberOfElements is an integer constant expression. An array whose size is defined by a constant expression we call a constant-length array. All the elements of the array are contiguous (or side by side) so that the size of the array is the size of each element multiplied by the number of elements in the array. Once an array is declared, its size is fixed and cannot be changed.

To declare an integer array of 10 elements, we use the following statement:

int anArray[10];

anArray is the base name of our array of 10 integers. This declaration creates 10 variables, each accessed via a single name, anArray, and an offset to each element within the range of 0..9.

We could also declare the size of the array using a preprocessor symbol, an enumeration constant, a variable, a variable constant, the result of a function call, or an expression.

In the following code sample, a preprocessor symbol is used to define the array size:

#define ARRAY_SIZE 10
int secondArray[ ARRAY_SIZE ]; 

In the following code sample, an enumeration constant is used to define the array size, as follows:

enum {
  eArraySize = 10
}
int thirdArray[ eArraySize ];

Note that for these three arrays, anArray, secondArray, and thirdArray, the numberOfElements component of the array definition can be determined and is known at compile time. Each of them has 10 elements whose offsets are in the 0..9 range. These are constant-length array definitions. The next array declarations declare variable-length arrays. Their size is determined by the value of a variable, an expression, or the result of a function call.

In the following code sample, we use a variable constant:

const int kArraySize = 10;
int fourthArray[ kArraySize ];

kArraySize is an integer whose value does not change. Here, we use a convention where constant values have k as their prefix. This is convenient because we can use kArraySize later when we want to access all the elements of the array.

Note that the const int data type is a variable type whose value is assigned at runtime and cannot be changed. The C compiler does not see any difference between a variable and constant variable they have been declared.

In the following code sample, a variable is used to define the array size:

int arraySize = 10;
int fifthArray[ arraySize ];

When we use a variable, we must understand that even if that value changes, the size of the array does not. The array size is set by the value of the variable at the time of its definition; the array size is fixed and cannot be changed after the array is defined, even though the variable value may later change.

In the following code sample, an expression is used to define the array size:

int someVariable = 5;
int sixthArray[ 2 * someVariable ];

As with a variable, when the array size can only be determined by evaluating an expression at runtime (that is, the expression contains one or more variables), the array size is set by the value of the expression at that time. Once the array has been allocated, its size is fixed and cannot change.

Let's say we wanted to keep track of the air pressure and tread depth for the wheels of various vehicles. If we had a getNumberOfWheels( enum VehicleKind ) function that returned an integer, we might want to declare our arrays for each of the wheels of a given vehicle using that function, as follows:

double tireTread[ getNumberOfWheels( tricycle ) ];
double tirePressure[ getNumberOfWheels( tricycle ) ];

getNumberOfWheels( tricycle ) would return 3. So, we would have declared two arrays of three double elements – one to hold values for each wheel's tireTread property and another to hold values for each wheel's tirePressure property. If, on the other hand, we needed to do this for an automobile, our use of the function to declare the arrays might look as follows:

double tireTread[ getNumberOfWheels( automobile ) ];
double tirePressure[ getNumberOfWheels( automobile ) ];

In the preceding code fragment, getNumberOfWheels( automobile ) would return 4. So, we would have declared two arrays of four double elements – one to hold values for each wheel's tireTread property and another to hold values for each wheel's tirePressure property.

Note that a function call is also an expression that can result in different values when the function is called.

Also, note that just as we cannot redefine the same name of a variable in the same function, we cannot use the same array names in the same function, even though they are of different sizes. The preceding declarations would have to occur in different places in our program or would have to have different names if they were in the same function.

Before going into further details, pay special attention to the difference between the definitions of anArray, secondArray, and thirdArray from the definitions of fourthArray, fifthArray, sixthArray, tireTread, and tirePressure. In the former set, the array sizes are constants that can be determined at compile time, whereas in the latter set, the array sizes cannot be determined at compile time and can only be evaluated when the program is running, at runtime. This is especially important when initializing each set of arrays. This is the essential difference between constant-length arrays and variable-length arrays.

Initializing arrays

As with all variables, it is important to initialize array elements to some known value before using them. Just as with structures, as we saw in Chapter 9, Creating and Using Structures, we can initialize an array in several different ways. We can initialize constant-length arrays differently than variable-length arrays. After initialization, these two types of arrays are otherwise identical.

The most basic way to initialize a constant-length array is to set all of its values to the same value at the time of definition. We set all the array elements to the same value, as follows:

int anArray[10] = {0};
int secondArray[ ARRAY_SIZE ] = {2};
int thirdArray[ eArraySize ] = {100};

All the values of each array are set to the same value given within { and }. All 10 values of anArray are set to 0, all 10 values of secondArray are set to 2, and all 10 values of thirdArray are set to 100. We can change the value of each element as needed later.

Note that we cannot initialize the variable-length arrays in this manner. We will see why in the Understanding variable-length arrays section.

To set each element to different values during initialization, each element's value can be specified between { and }, separated by commas, as follows:

int anArray[10] = { 2 , 4 , 6 , 8 , 10 , 12 , 14 , 16 , 18 , 20 };

anArray has set each element to an even number.

However, dynamically assigning an array size is possible if we don't first specify the number of elements. Even though the array size is not given (the array is temporarily an incomplete type), the compiler can determine the size from the values given. However, once the size is set in this manner, it cannot be changed. We can do this and let the number of given values determine the size of the array, as follows:

float lengthArray[] = { 1.0 , 2.0 , 3.0 , 4.0 , 3.0 , 2.0 , 1.0 };

In this definition, lengthArray contains seven floating-point values. Each of these values can later change, but the size of lengthArray will now always be seven values of the float type. We will see, in Chapter 15, Working with Strings, how this feature is essential to defining arrays of characters (strings).

Note that we cannot initialize the variable-length arrays in this manner, either. We will see why in the next section, Understanding variable length arrays.

Let's now see why we did not initialize the variable-length arrays declared earlier.

Understanding variable-length arrays

A Variable-Length Array (VLA) is an array whose size is not known until runtime. The first set of arrays declared earlier have fixed length declarations. The second set of arrays are VLA declarations. A VLA declaration is like a fixed-length declaration, except that the array size is specified by a non-constant expression. Here, non-constant refers to a non-literal variable value.

Remember that a const type is a variable whose value is determined at runtime, so even though it cannot be changed once initialized, it is still considered a variable and not a literal constant. When a VLA declaration is encountered, the size expression is evaluated and the array is created with the resultant length. This length must be a positive integer. Once created, the size of a VLA is fixed and cannot change, just as with constant-length arrays.

So, note that the variable in a VLA means that the array size is defined by some variable value and not that the size of the array may vary. C does not support arrays whose size may change after they are defined.

The main restriction for VLAs is that they may not be initialized when they are declared as constant-length arrays can be. We will see how to do this before we leave this chapter. The main benefit of VLAs is that they enable functions to operate on arrays much more flexibly.

Historical Note 1

VLAs were added to C99 and were a major feature of that version. While VLAs cannot be initialized as fixed-length arrays can, they are extremely useful when used within function parameters.

Historical Note 2

In C99, VLAs were a mandatory feature of the language. However, VLAs could not be reliably implemented due to memory constraints in certain environments. This, therefore, limited the adoption of C99. To remedy this, C11 made VLAs an optional feature of the language. Any implementation that does not support VLAs sets the __STDC_NO_VLA__ symbol to 1. This can then be tested by the programmer at runtime and handled appropriately, as follows:

#if __STDC_NO_VLA__  

  … Code that does not use VLAs.

#else  

  … Code that uses VLAs.

#endif

We can then determine the size of the array (if we know the data type of its elements) with the following program:

int main( void )  {
  int   anArray[10]  = {0}; // Initialize the whole thing 
                            // to 0.
  short typeSize   = sizeof( short );
  short arraySize  = sizeof( anArray );
  short elementNum = arraySize / typeSize;
  printf( "    sizeof(short) = %2d bytes
" , typeSize  );
  printf( "  sizeof(anArray) = %2d bytes
" , arraySize  );
  printf( "      anArray[] has %2d elements

" , elementNum );
     // Dynamically allocate array size via initialization.
  float lengthArray[] = { 1.0 , 2.0 , 3.0 , 4.0 , 3.0 , 2.0 
                         , 1.0 };
  typeSize   = sizeof( float );
  arraySize  = sizeof( lengthArray );
  elementNum = arraySize / typeSize;
  printf( "      sizeof(float) = %2d bytes
" , typeSize  );
  printf( "sizeof(lengthArray) = %2d bytes
" , arraySize  );
  printf( "    lengthArray[] has %2d elements

" , elementNum);
  // Allocate a VLA  
  const int kVLASize = 12;  long  vlArray[ kVLASize ]; 
  typeSize  = sizeof( long ); 
  arraySize  = sizeof( vlArray ); 
  elementNum = arraySize / typeSize; 
  printf( "      sizeof(long) = %2d bytes
" , typeSize  );
  printf( "   sizeof(vlArray) = %2d bytes
" , arraySize  );
  printf( "         vlArray has %2d elements

" , elementNum );
  return 0;
}   

With your editor, create an array1.c file and type in the preceding program. Compile it using the compiler options from the previous chapter and run it. You should see the following output:

Figure 11.1 – Screenshot of array1.c output

Figure 11.1 – Screenshot of array1.c output

anArray has a known size that was specified at its definition, initialized to 0. lengthArray has a dynamic size specified by the number of values given at its definition/initialization. By using sizeof() at runtime, the actual size of an array can be determined when the program runs.

There is another way to initialize any array – constant-length or variable-length – that is, element by element. Before we can do this, we must understand how to access each element. We will do this in the Accessing array elements section and the Operations on arrays with loops section when we will use a loop to access all elements of an array.

Accessing array elements

Each element of an array is accessed via its base name and an index into the array. An index is also known as a subscript. Each element is accessed using the following form:

arrayName[ index ]

Here, index is a value between 0 and the (array size minus 1). We can access any element of an array using this form. Just as with defining an array, the index may be a literal value, a variable, the result of a function call, or the result of an expression, as follows:

float anArray[10] = {2.0};
int   counter = 9;
float aFloat  = 0.0; 
aFloat = anArray[ 9 ];             // Access last element.
aFloat = anArray[ counter ];       // Access last element 
                                   // via value of counter.
aFloat = anArray[ exp( 3 , 2 )  ]; // Access element at 
                                   // result of function.
aFloat = anArray[ (sizeof(anArray)/sizeof(float) - 1 ]; 
                     // Access last element via expression.

Each of these statements accesses the last element of the array and assigns the value of that element to the aFloat variable. Note how the index is evaluated in several ways using a literal, a variable, a function result, and an expression. All of them evaluate to 9, the index of the last element of the array.

Now, it may seem odd that even though we declared an array of 10 elements, the index of the last element is not 10 but 9. To fully understand array indexing, it is critical to understand that the index is really an offset from the base name. We will henceforth use the array offset term whenever an index is intended. The first element of an array, therefore, has an offset of 0.

For example, we can declare an array of 4 integers, as follows:

int arr[4] = {0};  // 4 elements

Then, we can initialize each element of this array one by one, as follows:

arr[0] = 1;  // 0th offset, 1st element
arr[1] = 2;  // 1st offset, 2nd element
arr[2] = 3;  // 2nd offset, 3rd element
arr[3] = 4;  // 3rd offset, 4th element (last one)
arr[4] = 5;  // ERROR! there is no 5th element.

First, we can see how we can initialize an array element by element. For an array whose size is 4 elements, the valid range of offsets is 0 .. 3. This is called zero-based indexing, where the first element's index is 0. It is also sometimes referred to as the off-by-one problem, and it requires careful consideration since it can be a great source of programmer frustration. We originally encountered this in Chapter 7, Exploring Loops and Iterations, when examining the for()… loop. It is not actually a problem; some programmers might even consider it an intended design feature of C. It is really only a problem when we misunderstand it.

However, when we instead think of the index as an offset – the first element has no offset or a 0 offset – confusion is dispelled, and therein lies the difference. We will see in a bit how this off-by-one problem can actually work to our advantage when we loop through the whole array.

Note that it is up to the programmer to ensure that the array index is properly within the range of the array's dimension. The compiler may provide a warning if an array index is out of bounds, but it doesn't always do so.

Let's see when we can rely on the compiler and when we cannot with an example program. Copy array1.c into array2.c, delete main(), and then replace it with the following:

int main( void )  {
  int   anArray[10]  = {0}; // Initialize the whole thing to 0.
  int x, y , z;
  x = 11;
  y = 12;
  z = 13;
  anArray[ 11 ] = 7; // Compiler error!
  anArray[ x ]  = 0;  // No compiler error, but runtime error!;
  return 0;
}

Compile array2.c and run array2. You should see something like the following output:

Figure 11.2 – Screenshot of compiler error for array2.c

Figure 11.2 – Screenshot of compiler error for array2.c

The compiler sees that anArray[11] is out of array bounds and generates an error. Now, comment out the second-to-last line (anArray[11] ...). Again, compile and run array2. You should see something like the following output:

Figure 11.3 – Screenshot of runtime error for array2.c

Figure 11.3 – Screenshot of runtime error for array2.c

Here, anArray[x] does not cause a compiler error but does cause a runtime error, which is displayed as Abort trap: 6. The compiler cannot tell that the value of x holds a value that is out of bounds for the array at compile time. At runtime, however, an abort trap is generated.

This demonstrates that the compiler does not always detect array out-of-bounds errors. Therefore, we cannot rely on the compiler to always do so.

Assigning values to array elements

Once you can identify an element of an array, you can retrieve values from it or assign values to it, just as you would with any other variable.

To change the value of an array element, we can similarly use the following access forms to assign a value to it:

float anArray[10] = {0.0};
int   counter = 9;
float aFloat  = 2.5; 
anArray[ 9 ]                                  = aFloat;
anArray[ counter ]                      = getQuarterOf( 10.0 );
anArray[ pow( 3 , 2 )  ]                      = 5.0 / 2.0;
anArray[ (sizeof(anArray)/sizeof(float) - 1 ] = 2.5;

Each of these array-assignment statements assigns a value (evaluated in various ways) to the last array element, whose index is evaluated in various ways. In each case, the index is evaluated to 9, the index of the last element of the array. The pow() function raises the first parameter to the power of the second parameter, which in this case is 32 or 9. In each case, the value assigned to that array element is 2.5.

To summarize, each element of an array is a variable that we can get values from or assign values to via the array's name and its index.

Operating on arrays with loops

When an array is present, a common operation is to iterate over all of its elements. For this, we typically use the for()… loop. We could also use any of the other looping statements described in Chapter 7, Exploring Loops and Iterations. However, because, more often than not, we know the loop's size, it is easier and more reliable to use a counter-controlled for()… loop. When we explore arrays of characters (or strings) in Chapter 15, Working with Strings, we will begin our exploration of sentinel-controlled loops.

To iterate over the array, the index counter is initialized to 0 (to match the zeroth offset), the continuation expression specifies that the index counter remains fewer than the number of elements, and the index counter is incremented at each iteration. The loop is as follows:

const int kArraySize = 25;
int anArray[ kArraySize ];
for( int i=0 ; i < kArraySize ; i++ )  {   
// i: 0..24 (kArraySize-1)
  anArray[ i ] = i;
} 

anArray has 25 elements. The offsets range for each element in anArray goes from 0 to 24. In the loop statement, each element of the array is initialized to the value of its own offset. Note that we've made a note about the expected range of the value of the index counter, i, in that line's comment. For historical reasons that predate C and have more to do with another, older language, FORTRAN (now referred to as Fortran), the variable names – i, j, and k – are typically used as index counter variables. So, we often see these used as a common index-counting convention in C.

Note that the continuation expression, i < kArraySize, is rather concise. Here, the zero-based indices work together with the single less-than operator (<) to enable such compactness. There are other ways that this loop could have been expressed, but none are as compact as this. It is a common C idiom that had a valid design rationale when Central Processor Units (CPUs) were slower and simpler; this rationale is seldom valid today. Yet, it lives on. Indeed, this idiom characterizes the innate terseness of C.

Using functions that operate on arrays

An array is a collection of variables bound together by a common base name and an offset from that base. In nearly every respect, we can treat an individual element of an array just as we would any other variable. Even with function parameters, array elements can be passed into them as with regular variables, as follows:

#include <math.h>
int anArray[10] = {0};
anArray[3] = 5;
anArray[3] = pow( anArray[3] , 2 );

The fourth element of the array is assigned a value of 5. The function declared in math.h, pow(), is called with the value found in the fourth element of the array and is raised to the power of 2 (squared) and assigned back to the fourth element of the array, which now has a value of 25.

We could write a function that operations on anArray, as follows:

int find10Min( int anArray[10] );

This function prototype specifies that the function definition will only take arrays of size 10 and no other. This is perfectly fine if that is all we wanted to do. In fact, before C99, other mechanisms were used to create functions to operate on arrays of varying sizes; C99 made this easier.

We want to create functions that operate on all elements of an array, regardless of its size. But how do we use arrays of unknown sizes as parameters to functions? We can do this; arrays of unknown sizes can be passed as arguments to functions. We are specifying a VLA to do this. To pass an array of unknown size as a function parameter, we must declare that the function parameter is a VLA, as follows:

int findMin( int size, int anArray[] );
int findMax( int size, int anArray[] );
double findMean(   int size , int anArray[*] );
double findStdDev( int size , int anArray[*] );

In the last two function prototypes, [*] is equivalent to [] but explicitly states that the size of the array given is a VLA and will be known at runtime. The [*] syntax can only be used in function prototypes, not in function definitions.

C does not store the size of an array. We, as programmers, must keep track of that in order to ensure that we do not access memory beyond the bounds of the array. So, for the function declared previously, we pass the size of the array we are going to provide as one of the parameters. By doing this, the function can operate on a valid array of any size. This means that we don't have to write a function that operates on an array of 10 elements and another function to operate on an array of, say, 12 elements.

Next, we provide the type of each element – in this case, int – with the array name – in this case, anArray – and [] to indicate that the anArray identifier is actually an array. The size of the array is not relevant.

We have also provided two other function declarations previously that we will soon explore. In all of these declarations, an array is passed in whose size won't be known until run time, except by the passed-in size value.

Now, it might be assumed that C will copy the entire array into the called function as it does with other variables; however, it does not. Since that is not the case, arrays as parameters might appear to violate the rules of passing function parameters by copy, but they do not. How this mechanism works will become clear in Chapter 14, Understanding Arrays and Pointers. For now, we will take as given the fact that the array name is a named reference to a memory location and that the named reference is being copied into the function. Again, we will see, in Chapter 14, Understanding Arrays and Pointers, how the array name (a named reference to a memory location) and an offset from that location can become an individual array element. We will define the two functions given previously as follows:

int findMin( int size , int a[] )  {
  int min = a[0];  
  for( int i = 1 ; i < size ; i++ )
    if( a[i] < min )   min = a[i];
  return min;  
}
int findMax( int size , int a[] )  {
  int max = a[0];  
  for( int i = 1 ; i < size ; i++ )
    if( a[i] > max )  max = a[i];
  return max;  
}

In both functions, we set the min or max parameters to be the value of the first element (the zeroth offset element) and then compare every subsequent value to min or max. When a newly encountered value is greater than or less than the current min or max values, we update min or max. You might look at this and see that we are comparing a[0] to min or max, which has already been set to a[0], and think, "That's redundant." It is, but it also only requires an extremely small cost of computation. If you choose, you could modify these properties to start the loop from the first offset instead of the zeroth offset.

Finding the minimum and maximum values for an array is sometimes useful. So, too, is finding the mean and standard deviation for a set of values. Mean is the average value and is computed by finding the sum of all values and dividing it by the number of values. A loop that iterates over the array is indicated. The standard deviation is a value that shows how far from the mean the overall set of numbers is. A set with a small standard deviation has values that are mostly the same. A set with a large standard deviation has values that are all over the place. We would define those two functions as follows:

 
double findMean( int size , int a[] )  {
  double sum  = 0.0;
  for( int i = 0 ; i < size ; i++ )  {
    sum += a[i];
  }
  double mean = sum / size;
  return mean;
}
double findStdDev( int size , int a[] )  {
    // Compute variance.
  double mean     = findMean( size , a );
  double sum      = 0.0;
  double variance = 0.0;
  for( int i = 0; i < size ; i++ )  {
    sum += pow( (a[i] - mean) , 2 );
  }
  variance = sum / size;
    // Compute standard deviation from variance.
  double stdDev = sqrt( variance );
  return stdDev;
}

There are a few things to take note of in findMean() and findStdDev(). First, notice that the VLA indication is [] and not [*]. Next, notice that variables are declared as they are needed and are not necessarily all declared at the beginning of the function block. Next, notice that the pow() and sqrt() functions are math functions declared in math.h and are available in the C standard runtime library. We will have to use #include <math.h> to be able to compile our program. Lastly, recall that the names of function parameters in function prototypes are optional. The names of parameters that are actually used are found in the parameter list of the function definition.

To use these functions, create an array3.h file and add the function declarations there. Then, create an array3.c file and add the following main() portion of the program:

#include <stdio.h>
#include <math.h>
#include "array3.h"
int main( void )  {
  int array1[] = { 3 , 4 , 6 , 8, 13 , 17 , 18 , 19 };
  int array2[] = { 34 , 88 , 32 , 12 , 10 };
  int size = sizeof( array1 ) / sizeof( int );
  printf( "array1: range, mean, & standard deviation
" );
  printf( "    range = [%d..%d]
" , 
          findMin( size , array1 ) ,
          findMax( size , array1 ) );
  printf( "     mean = %g
" ,  findMean( size , array1 ) );
  printf( "  std dev = %g

", findStdDev( size , array1 ) );
  size = sizeof( array2 ) / sizeof( int );
  printf( "array2: range, mean, & standard deviation
" );
  printf( "    range = [%d..%d]
" , 
          findMin( size , array2 ) ,
          findMax( size , array2 ) );
  printf( "     mean = %g
" ,  findMean( size , array2 ) );
  printf( "  std dev = %g

", findStdDev( size , array2 ) );
  return 0;
}

In this program, two arrays – array1 and array2 – are dynamically defined and initialized. Note that when the functions that expect an array argument are called, only the array name is given. The details of the array are provided in the function prototype. Then, information about each of them is calculated and printed. Compile and run this program. You should see the following output:

Figure 11.4 – Screenshot of array3.c output

Figure 11.4 – Screenshot of array3.c output

In one case, we calculated the range, mean, and standard deviation of an integer array with eight dynamically assigned elements, and in another, we did the same calculations for a dynamically assigned integer array of five elements. Passing VLAs to these functions would be identical to how constant-length arrays are passed to them.

Summary

In this chapter, we learned how arrays are homogenous groupings of data types, unlike structures. We then learned how to declare arrays, as well as how to initialize them in various ways, depending on how they were declared. We learned the differences between constant-length arrays and VLAs for initialization and how they are identical in every other way. We saw that we don't always have to specify the size of an array if we initialize it with values when it is declared. Once an array has been declared, its size is fixed and cannot change. We learned how to access array elements via the element's offset, also known as the index or subscript. We further saw how to manipulate the simplest kind of array – a one-dimensional array – directly via the for()… loop, as well as by using arrays as function parameters.

This chapter is a prerequisite to Chapter 12, Working with Multi-Dimensional Arrays, through to Chapter 16, Creating and Using More Complex Structures, where various aspects of arrays will be explored with greater complexity. In Chapter 12, Working with Multi-Dimensional Arrays, we will examine how to declare, initialize, and manipulate arrays of two, three, and more dimensions. Chapter 13, Using Pointers, while not directly about arrays, is essential to understanding the relationship between arrays and pointers, which are explored in Chapter 14, Understanding Arrays and Pointers, which becomes the basis for Chapter 15, Working with Strings. In Chapter 16, Creating and Using More Complex Structures, we will complete our exploration of arrays with various kinds of arrays of structures and structures containing arrays.

Questions

  1. What are the three components of an array definition?
  2. What is a constant-length array definition?
  3. What is a variable-length array definition?
  4. Can you initialize a constant-length array when it is defined? Can you initialize a VLA when it is defined?
  5. Why are VLAs useful as function parameters?
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset