Skip to main content
Logo image

Dive Into Systems: Exercises

Section 2.5 Arrays in C

Exercise 2.5.1. Passing Array Parameters.

    Given the following code, what value would be printed?
    #include <stdio.h>
    
    void foo(float arr[], int size) {
        arr[10] = 2.0;
    }
    
    int main(void) {
        float a[20];
    
        a[10] = 1.0;
        foo(a, 20);
        printf("%f\n", a[10]);
    
        return 0;
    }
    
  • 0.0
  • Incorrect.
  • 1.0
  • Incorrect.
  • 2.0
  • Correct!
  • random, depending on what was in memory at location a[10] when the program was run.
  • Incorrect.
Hint.
Draw a stack diagram of what happens when foo is called
Answer.
A stack diagram is shown below:

Exercise 2.5.2. Computing a Base Address.

Given the following declaration, what is the address (in decimal) of arr[3] if the base address of array arr is 10000 (in decimal)? Assume a float is 4 bytes.
float arr[16];
Answer.
10000 + 4 * (3) = 10012.

Exercise 2.5.3. Memory Leaks.

    Given the following function that dynamically allocates and uses an array of floats, is there any code that needs to be added? Note that the array will never be used again after the function returns.
    int funct(int size) {
      float *arr;
      int result;
    
      // dynamically allocate array of float
      arr = malloc(sizeof(float) * size); 
      if(arr == NULL) {
        printf("malloc failed, exiting\n");
        exit(1);
      }
    
      // Use the array here
      result = do_something(arr, size);
    
      //all done using array at this point
    
      return result;
    }
    
  • Nothing needs to be added.
  • Incorrect. The array is no longer going to be used. What needs to be added?
  • yes, the function needs to set arr to NULL before returning
  • Incorrect! While good practice, this is insufficient.
  • yes, the function needs to free(arr) before returning
  • Correct!

Exercise 2.5.4. Dynamic Array Allocation.

Arrange the following statements in correct order to perform the following:
  • dynamically allocate an array, named arr, to store 10 integers
  • initialize every element of the array with a value of 1
  • free the array

Exercise 2.5.5. Find the Error.

    Given the following code:
    #include <stdio.h>
    #include <stdlib.h>
    
    #define SIZE (10)
    
    void update_array(int *a, int len) {
        int i;
    
        for (i = 0; i < len; i++) {
            a[i] += 1;
        }
    }
    
    int main(void) {
        int *arr;
        int *p;
        int i;
      
        arr = malloc(SIZE * sizeof(int));
        if(arr == NULL) {
            exit(1);
        }
        p = arr;
        for (i = 0; i < SIZE; i++) {
            arr[i] = i*i;
        }
    
        free(arr);
        printf("%d\n", p[5]);
    
        return 0;
    }
    
    Select all the statements that are FALSE:
  • Array arr was not initialized
  • Correct!
  • The output of the printf statement is undefined
  • No, this a true statement.
  • After the free() statement, the value of p is NULL
  • This is indeed false!
  • There is an error because the array index is out of bounds
  • Good! The array index is not out of bounds.
  • After the free() statement, the value of arr is NULL
  • Good! This not true either!

Exercise 2.5.6. Computing a Base Address.

Given the following array declaration, what is the address of arr[3][2], if the base address of arr is 10000 (in decimal)? Assume an int is 4 bytes.
int arr[16][32];
Answer.
Recall that in C, statically allocated 2D arrays are stored in row-major order. Since each dimension of the array contains 32 elements, and we are indexing row 3, we must first multiply column dimension (32) by row index (3) to get the address of the start of row 3. To access the element in column 2, we then must add 2 to access element arr[3][2]. Lastly, recall that ints are 4 bytes. The actual address is therefore the base address (in this case 10000) + 4 * (32*3 + 2), or 10392.

Exercise 2.5.7. Find the Error.

Answer.
The loop iterates from i = 0 to i = ROWS (inclusive), but since the array has a total of ROWS number of rows, the valid indices for the array are from 0 to ROWS - 1. Therefore, accessing array[rows][targetCol] is out of bounds and could lead to undefined behavior.

Exercise 2.5.8. Arrays as Function Parameters.

    Which of the following is the best and most general choice for a function parameter definition for passing a statically allocated 2-D array parameter?
  • init(float a[ ][100]);
  • Incorrect. Remember, we are looking for the best and most general!
  • init(float a[ ][100], int rows);
  • Correct!
  • init(float a[10][100]);
  • Incorrect. Remember, we are looking for the best and most general!
  • init(float a[ ][ ], int rows, int cols);
  • Incorrect. a cannot be fully empty!
  • init(float a[10][ ]);
  • Incorrect.
Hint.
The function could be called with arrays having a different number of rows
Answer.
The correct answer is B, though the compiler can generate code for solutions A, B, or C. Leaving the first dimension unspecified is an example of good, generic design since it allows any 2D array with column dimension 100 to be passed to the function, with the number of rows specified as the second parameter. The column dimension must be specified in the parameter definition of a 2D array so that the compiler can calculate the offset from the base address of the 2D array to the start of a particular row of elements. The offset calculation follows from the row-major order layout of 2D arrays in memory.

Exercise 2.5.9. Explore a larger program.

The next two questions give you an opportunity to practice writing functions related to 2D arrays. In both cases, we refer to the programming friend method of allocation.

(a)

Arrange the statements below to write a function named allocate_matrix that dynamically allocates and returns a 2D array using the programmer friendly method. If malloc fails, allocate_matrix should return NULL.
Here is what an example call to your function might look like:
int **array;
array = allocate_matrix(8, 10); // allocates a 8 x 10 matrix
if(array == NULL) {
    printf("allocate_matrix failed, exiting\n");
    exit(1);
}

(b)

Now, arrange the statements to write a function named free_matrix that deallocates the 2D array that was allocated by allocate_matrix above (using the programmer friendly method).
Here is what an example call to your function might look like:
int **array, rows;

// code to allocate and use array omitted

free_matrix(array, rows);