// arrays.cpp
#include <iostream>
#include "cmpt_error.h"
using namespace std;
void test1() {
const int N = 5; // It's a good idea to make the size of an array a const.
// This gives the size a name, and also makes sure that
// we always use the same size and never change it.
int score[N]; // Declares a sequence of N contiguous ints.
// The values of these ints are unknown and
// could be anything.
// We have no idea what values the following loop will print because score
// has been created but not initialized.
for(int i = 0; i < N; ++i) {
cout << "score[" << i << "] = " << score[i] << "\n";
}
// Set all the value of score to 0.
for(int i = 0; i < N; ++i) {
score[i] = 0;
}
// Now all the value of score are 0.
for(int i = 0; i < N; ++i) {
cout << "score[" << i << "] = " << score[i] << "\n";
}
// C++ does *not* check the bounds of an array, so you are free to access
// elements outside of an array. However, this is almost always an error,
// and it is up to the programmer to make sure they never do this.
cout << "\nscore[-1] = " << score[-1] << "\n";
cout << "\nscore[" << N << "] = " << score[N] << "\n";
}
// If you know the values of the array at the time you define it, you can use
// array initializer notation.
void test1a() {
int arr1[5] = {1, -2, 3, 0, 0};
int arr2[] = {1, -2, 3, 0, 0}; // C++ automatically infers the length of arr2
// These are for-each loops, which are in some cases simpler than regular
// for-loops.
for(int n : arr1) {
cout << n << "\n";
}
cout << "\n";
for(int n : arr2) {
cout << n << "\n";
}
}
// Let's write a small program that asks the user to enter some numbers, and
// then prints the average of them.
void test2() {
// get the size from the user
cout << "How many numbers do you want to enter? ";
int n;
cin >> n;
// make sure the size is 0 or higher
if (n < 0) {
cmpt::error("an array must have size 0 or more");
}
// create a new array of n uninitialized numbers
double nums[n];
// read in each number from the user
for(int i = 0; i < n; ++i) {
cout << "? nums[" << i << "] = ";
cin >> nums[i];
}
// calculate the average
double avg = 0.0;
for(int i = 0; i < n; ++i) {
avg += nums[i];
}
avg = avg / n;
cout << "\nAverage: " << avg << "\n";
}
// The C++ compiler determines the size of an array using it's size and value
// type:
//
// int score[5];
//
// This is an array of 5 ints, so it uses 5 * sizeof(int) bytes of memory.
//
// The number of elements in score is sizeof(score) / sizeof(int).
void test3() {
cout << " sizeof(char) = " << sizeof(char) << " byte\n";
cout << " sizeof(int) = " << sizeof(int) << " bytes\n";
cout << "sizeof(double) = " << sizeof(double) << " bytes\n";
const int N = 5;
char arr_char[N] = {'a', 'a', 'a', 'a', 'a'};
cout << "\narr_char uses " << sizeof(arr_char) << " bytes\n";
cout << "arr_char has " << (sizeof(arr_char) / sizeof(char)) << " elements\n";
int arr_int[N] = {0, 0, 0, 0, 0};
cout << "\narr_int uses " << sizeof(arr_int) << " bytes\n";
cout << "arr_int has " << (sizeof(arr_int) / sizeof(int)) << " elements\n";
double arr_double[N] = {0.0, 0.0, 0.0, 0.0, 0.0};
cout << "\narr_double uses " << sizeof(arr_double) << " bytes\n";
cout << "arr_double has " << (sizeof(arr_double) / sizeof(double)) << " elements\n";
}
// We can pass arrays to functions. Note that we don't include the size of the
// the array in the parameter argument so that we can use this with any size
// of array.
//
// Notice that we use "const" for the array parameter. This tells us, and the
// compiler, that the print does not modify arr in any way. It just reads it.
// If print (accidentally) changed a value in arr, then the compile would give
// an error message saying that you can't modify a const array.
void print(const int arr[], int size) {
if (size < 0) cmpt::error("array size can't be negative");
if (size == 0) {
cout << "{}";
} else if (size == 1) {
cout << "{" << arr[0] << "}";
} else { // size > 1
cout << "{" << arr[0];
for(int i = 1; i < size; ++i) { // note i starts at 1, not 0
cout << ", " << arr[i];
}
cout << "}";
} // if
}
void println(const int arr[], int size) {
print(arr, size);
cout << "\n";
}
void test4() {
const int N = 5;
int score[N];
println(score, N); // score is uninitialized: any values could be printed!
// Set all the value of score to 0.
for(int i = 0; i < N; ++i) {
score[i] = 0;
}
println(score, N); // prints all 0s
}
// You might wonder why the size of the array is passed as a parameter in
// print and println. Why not just calculate the size use sizeof, e.g.:
//
// void print(int arr[]) {
// int size = sizeof(arr) / sizeof(int);
// // ... same as before ...
// }
//
// This doesn't compile because the C++ compile cannot determine the number of
// elements in arr. All it knows is that arr refers to the start of an array,
// and that arr could have 0 or more elements. So we need to pass the size
// along as well.
//
// When arrays are passed as parameters, they are not copied. The function
// gets the actual array. This is efficient, and also convenient for functions
// that need to modify the array.
void fill(int arr[], int n, int size) {
for(int i = 0; i < size; ++i) {
arr[i] = n;
}
}
void test5() {
const int N = 5;
int score[N];
fill(score, 0, N); // set all values of score to 0
println(score, N); // prints all 0s
}
// It would be nice if we could write a function that returns a newly
// created array, e.g.:
//
// int[] make_int_array(int size) {
// int arr[size];
// for(int i = 0; i < size; ++i) {
// arr[i] = 0;
// }
// return arr;
// }
//
// Unfortunately, this doesn't compile because C++ forbids functions from
// returning arrays. However, it is possible to return pointers to arrays,
// which is practically the same thing, and we'll see that when we discuss
// pointers.
int main() {
// test1();
// test1a();
// test2();
// test3();
// test4();
test5();
}