Laboratory: Pointers, passing arrays to functions
Gábor Horváth / Zsolt Kohári · 2020.10.21.
Pointers, passing arrays to functions.
We will need pointers to solve the problems of this lab. Either because of indirect parameters, or because of arrays.
When passing an array as a function parameter, its memory address is passed just like if
&t[0]
was passed. For this reason usually the size of the array must be passed,
too.
1. Where do pointers point to?
Look at the short code below with a few variables: a real and two pointers. The drawing shows the state of the variables when the program reaches the line denoted by 1 .
int main(void) { double d1 = 3.14; double *p1, *p2; p1 = &d1; p2 = NULL; return 0; // 1 }
Make similar drawings for each numbered bubble of the program below: what are the variables and where the pointers point to! The bubbles are numbered in the order of their execution. Figure out what the program prints without running it! At last run it to test your result.
#include <stdio.h> void func1(int i2) { i2 *= 2; printf("func1()... i2 = %d\n", i2); // 4 } void func2(int *p3) { *p3 *= 2; printf("func2()... *p3 = %d\n", *p3); p3 = NULL; // 5, 6 printf("func2()... p3 = %p\n", p3); } int main(void) { int i1 = 2; int *p1 = NULL; int *p2 = NULL; printf("Where do p1, p2 point to?\n"); // 1 p1 = &i1; printf("&i1 = %p, p1 = %p\n", &i1, p1); printf("i1 = %d, *p1 = %d\n", i1, *p1); // 2 i1 = 3; printf("*p1 = %d\n", *p1); *p1 = 4; printf("i1 = %d\n", i1); p2 = p1; *p2 = 5; printf("i1 = %d, p2 = %p\n", i1, p2); // 3 printf("-----\n"); func1(i1); printf("main()... i1 = %d - but why?\n", i1); printf("-----\n"); func2(&i1); printf("main()... i1 = %d - why?\n", i1); printf("-----\n"); func2(p2); printf("main()... i1 = %d - why?\n", i1); printf("main()... p2 = %p - why?\n", p2); return 0; }
2. Cube – passing a parameter by address
Write a function that receives the length of an edge of a cube as a parameter. The function should return the volume and the surface area of the cube in two further parameters passed by address. (The return type of the function is void.)
Call this function from the main to calculate the volume and the surface area of a cube of
2.7
edge length, then print the results in main!
Reminder
In case of a parameter passed by address the function can modify the value of the passed variable.
This method is often referred to as „returning the result via parameter list.” Remember the printf()
–scanf()
functions: scanf()
should modify the passed variable, printf()
should not.
For this reason scanf()
takes the parameter by address, and printf()
by value:
int x; scanf("%d", &x); printf("%d", x);
More than hint
void cube(double edge, double *parea, double *pvolume);should be the prototype of the function.
Solution
#include <stdio.h> #include <math.h> void cube(double edge, double *parea, double *pvolume) { *parea = pow(edge, 2) * 6; *pvolume = pow(edge, 3); } int main(void) { double a, v; cube(2.7, &a, &v); printf("V=%f A=%f\n", v, a); return 0; }
Write a function that receives two numbers, and return both their sum and their product!
Solution
The problem is that in the C language functions can return a single value only. One possibility to overcome this restriction is to use a structure to encapsulate the two return values. The other possibility, that is widely used in the practice, is to use function parameters to return multiple results of the function. In this case these function arguments must be passed by address. Instead of returning the result, the function is going to write the results to the variables whose pointers were passed on the argument list.
void calculate(int a, int b, int *sum, int *product) { *sum = a + b; *product = a * b; } int s, p; calculate(9, 11, &s, &p);
Of course, the last two function arguments are also passed by values, but the value
is a memory address. The type of variables sum
and product
are
pointers pointing to int
data.
4. Array search
Create a function that receives an array of integers as parameter, and one integer value that has to be found in the array! Return the index of the element if the value was found, or return −1, if it was not. (If the value occurs multiple times in the array, it is up to you which index you return.)
Print the array with the indices to check the result of your function. Print the index returned by the function, or the text „not found”!
Reminder
When passing a simple value to a function, the value will be copied into the parameter.
When passing an array, the array will not be copied! The function just receives a pointer
to the beginning of the array either using an int t[]
or an
int *t
definition for the parameter. Both forms are correct for function parameters,
use what you prefer. We should not forget that in this context both forms refer to a pointer.
This is the very reason why the size of the array must also be passed.
Modify the function (and the program) to return the memory address, instead of the index of the found element! What should be returned when the value was not found in the array? Using the modified function how can you determine the index of the found element (without another search)?
Reminder
p1 + integer = p2
: a pointer to the beginning of the array + an integer value → a pointer to a certain element of the array.
For this reason p2 - p1 = integer
is also fine, gives how many elements must be skipped to get from one memory address to another.
Modify the loops in the program to use pointers instead of indices.
Reminder
If p
is a pointer, p + 1
is correct, then p = p+1
is correct, too.
Then p += 1
and ++p
are correct as well.
This is the way to step in the loops.
Solution
#include <stdio.h> int search(int *array, int n_elem, int to_find) { int i; for (i = 0; i != n_elem; ++i) if (array[i] == to_find) return i; return -1; } int *search_ptr(int *array, int n_elem, int to_find) { int i; for (i = 0; i != n_elem; ++i) if (array[i] == to_find) return array+i; return NULL; } int *search_ptr_arithm(int *array, int n_elem, int to_find) { int *p; for (p = array; p != array+n_elem; ++p) if (*p == to_find) return p; return NULL; } int main(void) { int values[10] = { 5, 8, 3, 6, 9, -2, 4, 6, 7, 1 }; int to_find = -2; int index = search(values, 10, to_find); if (index != -1) { printf("%d has an index of: %d\n", to_find, index); } else { printf("%d not found\n", to_find); } int *p1 = search_ptr(values, 10, to_find); if (p1 != NULL) { printf("%d is at the memory address: %p, has the index: %ld\n", to_find, p1, p1-values); } else { printf("%d not found\n", to_find); } int *p2 = search_ptr_arithm(values, 10, to_find); if (p2 != NULL) { printf("%d is at the memory address: %p, has the index: %ld\n", to_find, p2, p2-values); } else { printf("%d not found\n", to_find); } }
Write a function that receives an integer array, and separates the even and the odd elements of the array. The even elements are put into a second array, and the odd ones are put into a third array; both of them are function arguments, and are assumed to be large enough to store all even and odd elements.
Write a function to print the integer array received as a function parameter!
Write a main function that separates the even and odd elements of an array and prints them to the screen!
Hint
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 even and odd numbers were found, otherwise it does not know how many elements to print from those arrays.
Solution
The algorithm for separating the elements is very simple. 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 even numbers. It is increased when a new even number is found.
- The index for the array of odd numbers, increased when a new odd number is found.
Observe that when printing an array we dont pass the real capacity of arrays even[]
and odd[]
,
but the number of useful elements in those arrays. At the declaration, the capacity of these arrays must be big enough for
the worst case, when all elements of the input array are even or odd.
#include <stdio.h> void separate(int *in, int in_len, int *out_even, int *out_odd, int *out_even_len) { int i_even = 0, i_odd = 0; for (int i = 0; i != in_len; ++i) { if (in[i] % 2 == 0) { out_even[i_even++] = in[i]; } else { out_odd[i_odd++] = in[i]; } } *out_even_len = i_even; } 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 even[10], odd[10]; int even_len, odd_len; separate(numbers, 10, even, odd, &even_len); odd_len = 10 - even_len; printf("Even numbers:\n"); array_print(even, even_len); printf("Odd numbers:\n"); array_print(odd, odd_len); return 0; }
6. Further problems for practicing
At least twice
Write a function that receives an array of integers as parameter and returns the address of the first element, which occurs at least twice in the array. Return a NULL pointer if there is no duplicate value!
Sorted?
Write a function that receives an array of real values as parameter. There are no duplicate values in the array. The function should check whether the values in the array are in a sorted order (either ascending or descending order). Looking at the first two elements, the way of possible sorting can be determined. The function should return the address of the first such element that breaks the ordering rule. Return NULL if the array is sorted. Examples: for {-8.11, -5.3, 0.1, 2.5, 1.4, 6.9, 12.0, 5.7} the return value should be the address of the element containing 1.4; for {7, 1, 2, 3, 4, 5} the return value should be the address of the element containing 2.
Unique values
Write a function to count the different unique values (no duplicate) in the array. Like in { 2, 7, 5, 8, 9, 5, 7, 5, 5, 3 } there are 4: { 2, 3, 8, 9 }.