Classroom practice: Pointers
Gábor Horváth / Zsolt Kohári · 2022.10.17.
Function arguments by address. Arrays with functions.
Preparation for the practice:
- The lecture slides on arrays should be reviewed.
- The lecture slides on pointers should be reviewed.
Write a function that receives an integer array, and separates the greater and the smaller than the average elements of the array. The small elements are put into a second array, and the great ones are put into a third array.
Write a function to print the integer array received as a function parameter!
Write a main function that separates the great and small elements of an array and prints them to the screen!
Solution
The algorithm for separating the elements is simple, first we get the average, after that, we need to put the elements in the arrays. We have three arrays, hence we will need three extra variables to index the arrays (to know where we are currently in the arrays):
- The index for the input array. This index increases by one in every step.
- The index for the array of great numbers. It is increased when a new great number is found.
- The index for the array of small numbers, increased when a new small number is found.
Notice that passing an array to a function means passing pointer to its first element, thus we need to pass the length of the array in a separate function parameter. Furthermore, somehow we have to return to the calling function how many small and great numbers were found, otherwise it does not know how many elements to print from those arrays.
Observe that when printing an array we dont pass the real capacity of arrays small[]
and great[]
,
but the number of useful elements in those arrays. During the declaration, the capacity of these arrays must be big enough for
the worst case, when all elements (except one) of the input array are great or small.
#include <stdio.h> void separate(int *in, int in_len, int *out_small, int *out_great, int *out_small_len) { int sum=0; for (int i = 0; i < in_len; i++) { sum+=in[i]; float avg=(float)sum/in_len; int i_small = 0, i_great = 0; for (int i = 0; i < in_len; i++){ if (in[i] < avg){ out_small[i_small++] = in[i]; } else { out_great[i_great++] = in[i]; } } *out_small_len = i_small; } void array_print(int *in, int in_len) { for (int i = 0; i != in_len; ++i) printf("%d, ", in[i]); printf("\n"); } int main(void) { int numbers[10] = { 1, 7, 2, 6, 9, 3, 4, 5, 9, 1 }; int small[9], great[9]; int small_len, great_len; separate(numbers, 10, small, great, &small_len); great_len = 10 - small_len; //it will not work if we do not allow equality! printf("small numbers:\n"); array_print(small, small_len); printf("Great numbers:\n"); array_print(great, great_len); return 0; }
The task is to reverse an array of integers. Let us write two different variants of the solution. According to the first approach, the reversing function receives two arrays, one is the input (the original), and the other is the output (the reversed). The second approach should use only a single array, that contains the input array initially, and the function reverses it in-place, by modifying the content of the input array.
Solution
The two solution approaches lead to very different solutions. The one where the output is directed to a separate array mostly consists of copy operations, such that the input array is traversed backwards, while the output array is built forwards. The inplace reverse algorithm is a bit more tricky. In each step, two integers have to be swapped (first - last, second - before the last, etc.). Take care to stop swapping in the middle, otherwise the already reversed array is reversed back again.
#include <stdio.h> void reverse_to_array(int *in, int *out, int len) { /* goes backwards and puts all the elements to the output array */ int i = len - 1; int j = 0; while (i >= 0) { out[j] = in[i]; ++j; --i; } } void reverse_inplace(int *arr, int len) { /* going till the middle */ for (int i = 0; i != len/2; ++i) { /* arr[i] <-> arr[len-1-i] */ int temp = arr[i]; arr[i] = arr[len-1-i]; arr[len-1-i] = temp; } } void array_print(int *in, int in_len) { for (int i = 0; i != in_len; ++i) printf("%d, ", in[i]); printf("\n"); } int main(void) { int original[] = {3, 5, 2, 4, 6, 1, 2, 1, 0, 9}; int target[10]; reverse_to_array(original, target, 10); print_array(target, 10); reverse_inplace(target, 10); print_array(target, 10); return 0; }
Assume we have a database (an array) of competitors, storing the name, the birth date and the rank.
typedef struct Date { int year, month, day; } Date; typedef struct Competitor { char name[31]; Date birth; int rank; } Competitor;
The task is to write a function that filters the database: the function is given the array of competitors and a target array, where it places the competitors born before 2000 and ranked in the top 100.
Solution
Note that the function has to return the length of the number of valid elements placed into the target array.
#include <stdio.h> typedef struct Date { int year, month, day; } Date; typedef struct Competitor { char name[31]; Date birth; int rank; } Competitor; int filter_competitors(Competitor *in, Competitor *out, int len) { int out_len = 0; /* stores the number of elements in the target array*/ for (int i = 0; i < len; ++i) if (in[i].birth.year < 2000 && in[i].rank >= 100) out[out_len++] = in[i]; return out_len; } void print_competitor(Competitor c) { printf("%s, %d.%d.%d., %d\n", c.name, c.birth.year, c.birth.month, c.birth.day, c.rank); } int main() { Competitor competitors[5] = { { "Am, Erica", {1984, 5, 6}, 1 }, { "Abnorm, Al", {1982, 9, 30}, 3 }, { "Pri, Mary", {1988, 8, 25}, 2 }, { "Duck, Ling", {1979, 6, 10}, 5 }, { "Mac, Donald", {1992, 4, 5}, 4 }, }; Competitor filtered[5]; int filt_len; filt_len = filter_competitors(competitors, filtered, 5); for (int i = 0; i < filt_len; ++i) print_competitor(filtered[i]); return 0; }