Sum Values - With Memory
Suppose we wanted to alter Sum Values program from the previous chapter. Instead of simply providing the sum, we would like to echo the actual values entered as an expression in the ordered the values were entered. To ensure that the expression is overly long, we will restrict the number of values to at most 20 numbers.
Here is a sample run of this altered program:
Please enter a number (0 to exit): 15
Please enter a number (0 to exit): 5
Please enter a number (0 to exit): -2
Please enter a number (0 to exit): 0
15 + 5 + -2 = 18
What we need:
- Prompt and read number repeatedly storing numbers until value is read
- Print the stored values with + sign between them as well as the result.
How do we do this?
How would we solve this problem? In the previous version of this problem, we had to keep a running sum but now we also need to track the actual input. Now, potentially we can declare 20 separate variables to do this... We would need to initialize it all to 0. Then during the output stage, we would need to check if the number is 0 before doing the output..
...
int num1 = 0;
int num2 = 0;
int num3 = 0;
//declare more numbers here ...
printf("Please enter a number (0 to exit): ");
scanf("%d", &num1);
if (num1 != 0){
printf("Please enter a number (0 to exit): ");
scanf("%d", &num2);
if (num2 != 0){
printf("Please enter a number (0 to exit): ");
scanf("%d", &num3);
if(num3 != 0){
...
}
else{
printf("%d + %d = %d", num1, num2, num1+num2);
}
}
...
}
else{
no numbers entered
}
...
Yucky for sure... maybe you can make this work for 20 numbers...However, what if our spec had restricted the problem to 200 numbers? what if it was 2000? what if it was 20000?
As the amount we allow increase, this approach will quickly be too large to implement correctly. Therefore we need a completely different approach.
Arrays
An array is a data structure. That is it is a way to organize and access a large amount of data. To declare an array you can use the following syntax:
datatype arrayName[capacity];
- datatype is some data type such as int, char, double etc. This specifies what is held in each element of the array.
- arrayName is the variable name for the array.
- capacity is the number of elements reserved for the array.
For example:
int numbers[5];
In the above statement we create 5 integers in one statement. The 5 integers that are part of this array live side by side in memory. Each element has a numeric index that refers to the element. The first element is indexed at 0. Each element has an index that is 1 value higher. To access a particular element of the array, we supply its index in square brackets.
You can picture the above array as follows:
The indexes of the array is what makes this method different from declaring a bunch of separate integer variables. An index is a numeric value and it is not a part of the name itself. Thus, we can store the index we wish to access into a variable and alter it as needed. If we declare a variable like num1, the 1 in the name is part of the name and that name if fixed and not adjustable. Understanding this is understanding the power of the array.
When you declare an array, the contents of that array are not defined. This is similar to variables not being defined. It is good practice to initialize the every element to 0. We can do this using the following syntax:
datatype arrayName[capacity] = {0};
the {0}
means that we put 0's into every element of the array.
Thus,
int numbers[5] = {0};
will produce:
Here is a block of code that will declare an array and read numbers into the array and print them out:
//define how big our array is using a macro. This way we can adjust this maximum value in one place
#define CAPACITY 5
...
//this is the array
int numbers[CAPACITY]={0};
//this is a variable that tracks how many things are stored in the array. Initially there is nothing.
int used = 0;
//a variable used to read the data so that we don't alter array unless we know there is space
int input;
//prompt and scan
printf("Please enter a number (0 to exit): ");
scanf("%d",&input);
//used < CAPACITY ensures that we do not put more than CAPACITY things into array. Check that first
//input != 0 is the user input piece
while(used < CAPACITY && input != 0 ){
numbers[used] = input;
printf("Please enter a number (0 to exit): ");
scanf("%d",&input);
used++;
}
This animation shows how the program will run if the user entered 42, 17, 8, 99, 3 and 0
Variable used is 0, so when we store the first number, it goes into numbers[0]. Notice how we use the used variable to locate where to put something. After storing, we increment used to 1. The second number goes into numbers[1], and so on.
There are two conditions we use in this loop.
- used < CAPACITY prevents us from writing information past the end of the array which can cause a crash
- input != 0 indicates that we are done reading our information
We want to avoid accessing an element outside the bounds of our array. In our program, you will notice that we use an extra variable input to help us with this. Consider what would happen if we restructured our program to read directly into the array.
#define CAPACITY 5
...
//this next line states that the array has 5 elements because CAPACITY is 5
//valid indexes are 0 to 4 inclusive
int numbers[CAPACITY]={0};
int used = 0;
printf("Please enter a number (0 to exit): ");
scanf("%d",&numbers[used]);
while(used < CAPACITY && input != 0 ){
used++;
printf("Please enter a number (0 to exit): ");
//this next line can potentially cause a crash!
//if we have read in 5 numbers already or used is 5
//numbers[used] is out of range!
scanf("%d",&numbers[used]);
}
The above block of code is syntactically correct BUT, it has a huge error where there is a potential that we will read a value past the end of the array! These types of errors are some of the hardest to find because it might cause something weird to happen or it might not...Errors that only show up sometimes are the worst!
Functions Involving Arrays
When we want to use an array in a function, we need to pass the array into it in the same way that we would pass other pieces of data.
We can do this using the following syntax in the parameter list
... functionName(...datatype arrayName[]...)
In other words we add [] after the arrayName to indicate that the parameter is suppose to be an array as opposed to a regular variable.
One difference between passing regular variables and arrays into a functions is that, we do not duplicate arrays. This is because arrays can be large data structures. Duplicating the entire array is potentially very expensive computationally. This means that within a function, any changes to an array parameter will change the calling argument. There is actually more to the situation than this. We will cover this in a later chapter. For now, it is good enough to know that any changes to made to an array parameter will result in changes to the array used in the function call.
readNumbers() function
Putting all this together we can shape our program thus far as follows:
#include <stdio.h>
//our spec stated that we expect 20 numbers
#define CAPACITY 20
//this function reads from the user a set of numbers and stores it into an array. Reading stops
//after CAPACITY values are read or after user enters a 0. Function returns number of values read
int readNumbers(int numbers[]);
int main(void)
{
//declare an array with CAPACITY elements
int numbers[CAPACITY]={0};
int used = readNumbers(numbers);
return 0;
}
int readNumbers(int numbers[])
{
int used = 0;
int input;
printf("Please enter a number (0 to exit): ");
scanf("%d", &input);
//continue reading while we have space in array and user hasn't entered 0
while(used < CAPACITY && input != 0) {
numbers[used] = input; //store the number in array
used++; //increment count of stored numbers
printf("Please enter a number (0 to exit): ");
scanf("%d", &input);
}
return used;
}
Calculating the sum:
Once we have read in the the numbers, we want to be able to calculate the sum of all the numbers. Whenever we use arrays, we need to keep track of how much of the array is actually being used. This is not the same as the capacity! Think of the situation like a a bottle of water. Your bottle may be able to hold 500 ml of water. However, if you drink half of it, the amount of water in the bottle is only 250ml. The bottle can still hold 500ml but the actual amount of water is not 500ml. Same goes for arrays. Thus, when we write functions that process existing arrays, we often need to include the amount of the array that is being used.
Earlier we stated that any array passed to a function can be modified within a function. In this case, we do not want that to happen as adding up the values in the array should not modify the array. We can be careful and ensure that our code does not accidentally modify the array.. but errors happen. We never intentionally put them in but they still get added. To ensure that any modification to an array gets flagged by the compiler (instead of just letting you make the error letting you figure out where you went wrong) we can add the const keyword to the argument. If your code attempts to modify the array in your function , the compiler will tell you that you are trying to change something that should not be changed.
//this function sums the numbers in the numbers array
//used is the number of values stored in the numbers array
int sumArray(const int numbers[], int used)
{
int total = 0;
int i;
//note this pattern for a for loop that will iterate through every element of the array
//this is why we count from 0!
for(i = 0; i < used; i++){
total += numbers[i];
}
return total;
}
Printing the Expression
The last step of our program is to print our expression. We have our array and we essentially want to print a bunch of + signs between the numbers.
Consider an array that has the values: 42, 3 and 6
We would want to print:
42 + 3 + 6
Note the number of + signs printed is 1 less than the number of elements in the array. There are two ways we can think about this problem. We can think of the plus signs as something we print just before every number other than the first or we can print the plus signs after every number except the last. Depending on how you think about the problem you will print the numbers differently.
Here are the two ways you can implement the expression... which way you choose.. thats up to you
plus sign comes before every number except the first
void printExpression(const int numbers[], int used, int total)
{
int i;
//print the first number
printf("%d", numbers[0]);
//start with the second number and print everything else in array
for (i = 1; i < used; i++){
printf(" + %d", numbers[i]);
}
printf(" = %d\n", total);
}
plus sign comes after every number except the last
void printExpression(const int numbers[], int used, int total)
{
int i;
//start with the second number and print everything else in array
for (i = 0; i < used-1; i++){
printf("%d + ", numbers[i]);
}
//print last number
print("%d", numbers[used-1])
printf(" = %d\n", total);
}
Putting it all together
We can put all of this together for a full program. Note that our main has to do a small check to ensure that at least one number was entered or the output would be wrong.
#include <stdio.h>
//our spec stated that we expect 20 numbers
#define CAPACITY 20
//this function reads from the user a set of numbers and stores it into an array. Reading stops
//after CAPACITY values are read or after user enters a 0. Function returns number of values read
int readNumbers(int numbers[]);
//this function sums the numbers in the numbers array
//used is the number of values stored in the numbers array
int sumArray(const int numbers[], int used);
//this function prints the sum expression with the total
void printExpression(const int numbers[], int used, int total);
int main(void)
{
//declare an array with CAPACITY elements
int numbers[CAPACITY]={0};
int used = readNumbers(numbers);
int total;
if(used != 0){
total = sumArray(numbers, used);
printExpression(numbers, used, total);
}
else{
printf("no values entered");
}
return 0;
}
int readNumbers(int numbers[])
{
int used = 0;
int input;
printf("Please enter a number (0 to exit): ");
scanf("%d", &input);
//continue reading while we have space in array and user hasn't entered 0
while(used < CAPACITY && input != 0) {
numbers[used] = input; //store the number in array
used++; //increment count of stored numbers
printf("Please enter a number (0 to exit): ");
scanf("%d", &input);
}
return used;
}
//this function sums the numbers in the numbers array
//used is the number of values stored in the numbers array
int sumArray(const int numbers[], int used)
{
int total = 0;
int i;
//note this pattern for a for loop that will iterate through every element of the array
//this is why we count from 0!
for(i = 0; i < used; i++){
total += numbers[i];
}
return total;
}
void printExpression(const int numbers[], int used, int total)
{
int i;
//print the first number
printf("%d", numbers[0]);
//start with the second number and print everything else in array
for (i = 1; i < used; i++){
printf(" + %d", numbers[i]);
}
printf(" = %d\n", total);
}