6. Arrays
By Joker. February 18th, 2020. 2:20 PM
Check the guide's index here.
Objectives- What is an Array
- Array declaration
- Initializing arrays
- Indexes and element positions in an array
- Matrixes and multidimensional arrays
- Defining constants in C
- Differences between const and #define
- Generating random numbers
Intro
After learning about C's basic data types (char, int, float and double) and its control structures (if, switch, while, for and do ... while), we're now going to study the way how we can process sets of data/values of the same type.
An array is nothing more than a set of consecutive elements, all of the same data type, which can be accessed individually from a single name.
As examples of using arrays, we can present:
- Set of monthly comissions associated to a certain employee throughout the span of a year.
12 000 5 000 2 300 1 230 7 400 ... - Array of Mr. Justin's Comissions (Simple Integer Array).
- Each position corresponds to the value received in that month (Ex: 12 000 - january, 2 300 - march, etc.).
This way, you avoid declaring a variable for each month (12 variables on total), placing all information in one single variable.
- Tic Tac Toe (Two Dimensional Character Array)
X O X O
Declaring Arrays
An array in C is declared the same way as a simple variable.
int n; /* Declaration of the variable n */
Single dimension array declarations obey the following syntax:
- type - corresponds to the data type of each of the array's elements.
- variable_name - indicates the name by which that array will be recognized as.
- elem_num - constant value which indicates how many elements that array has.
To declare an array with 20 integers:
int g[20]; /* g is an array with 20 integers */
To declare an array with 100 floating point numbers:
float rent[100]; /* rent is an array with 100 integers */
Every element of an array can be identified by the same name (g or rent, in the previous examples), however, in order to identify each of them individually, you need a number (index) to indicate what is its position in the array.
In a declaration like
float rent[100]; /* rent is an array with 100 integers */
- float - Type of each of the array's elements.
- 100 - Number of elements in the array.
- rent - The array's name.
- rent[i] - Value stored in the rent array's i position.
Let us suppose, then, that we pretended to declare an array with six integers, named array.
int array[6];
This way, each of the our array's six positions can be accessed by placing the respective index inbetween square brackets [].
Example: Place the value 123 in the array's first position.
array[0] = 123;
Place the double of the array's first element's value on the last position.
array[5] = array[0]*2;
Place the sum of the first and last elements on the array's third element.
array[2] = array[0] + array[5]; /* Notice that the third element is contained on the index 2 position, * because the array's elements start on the index 0 */
The position occupied by an array's element is also called that array element's index.
Initiate the array again, placing, in each position, that position's corresponding index.
for(i = 0; i < 6; i++) array[i] = i;
An element's index can be represented by any expression that returns an integer value.
array[1] = 2; /* 1 is an integer */ array[3 - 1] = 23; /* equivalent to array[2] = 23 */ array[1 + array[1]] = 513; /* equivalent to array[1 + 2] = 513 */ /* because array[1] is an integer */
Automatically Initializing Arrays
It's possible to automatically initialize every element in an array through following syntax:
Example: Declare and initiate an array with all vowels in the alphabet.
char vowel[5] = {'a', 'e', 'i', 'o', 'u'};
This way, we avoid having to write more code than needed:
char vowel[5]; vowel[0] = 'a'; vowel[1] = 'e'; vowel[2] = 'i'; vowel[3] = 'o'; vowel[4] = 'u';
Let's suppose the following declaration:
int v[10] = {10,20,30};
In the previous example, the array's three first elements (indexes 0, 1 and 2) are initiated with the values 10, 20 and 30, respectively, and all other elements are initiated with the value 0.
Thus, the following instructions are equivalent
int v[10] = {10, 20, 30}; int v[10] = {10, 20, 30, 0, 0, 0, 0, 0, 0, 0};
Example: Write a program that reads the wages paid to an individual during a whole year. Next, the program should show the monthly values and the annual total.
prog0601.c1: #include <stdio.h> 2: 3: int main(){ 4: float wag[12]; /* 12 months */ 5: float total; 6: int i; 7: 8: for(i = 0; i < 12; i++){ 9: printf("Input the %dth month's wage: ", i+1); 10: scanf("%f", &sal[i]); 11: } 12: 13: /* Show the monthly values and calculate the total */ 14: puts(" Month Value "); 15: for(i = 0, total = 0.0; i < 12; i++){ 16: printf(" %5d %9.2f\n", i+1, sal[i]); 17: total += sal[i]; 18: } 19: 20: printf("Annual Total: %9.2f\n", total); 21: return 0; 22: }
$ ./prog0601 Input the 1th month's wage:1000 Input the 2th month's wage:2000 Input the 3th month's wage:3500 Input the 4th month's wage:6480.5 Input the 5th month's wage:32000 Input the 6th month's wage:4560.5 Input the 7th month's wage:10000 Input the 8th month's wage:7450 Input the 9th month's wage:6471 Input the 10th month's wage:1240 Input the 11th month's wage:1240 Input the 12th month's wage:1240 Month Value 1 1000.00 2 2000.00 3 3500.00 4 6480.50 5 32000.00 6 4560.50 7 10000.00 8 7450.00 9 6471.00 10 1240.00 11 1240.00 12 1240.00 Annual Total: 77182.00
Notice that the user doesn't need to know how the data is stored in our program. Therefore, when we ask the first month's wage (month 1), we'll have to store it in our array's 0 position. It doesn't make sense to solicit the user for the 0 month's wage. Because of that, we write the value i + 1 on screen and storing the value on the i index of the array.
8: for(i = 0; i < 12; i++){ 9: printf("Input the %dth month's wage: ", i+1); 10: scanf("%f", &sal[i]); 11: }
The previous example can cause some confusion, because we're used to thinking of months as values between 1 and 12, and not between 0 and 11.
Because of that, some programmers solve the problem in another way.
They declare an array with 13 elements (indexes 0..12), in order to use the indexes relative to the pretended month (1...12). The 0 index is simply ignored.
prog0602.c1: #include <stdio.h> 2: 3: int main(){ 4: float wag[12]; /* 12 months */ 5: float total; 6: int i; 7: 8: for(i = 1; i <= 12; i++){ 9: printf("Input the %dth month's wage: ", i+1); 10: scanf("%f", &sal[i]); 11: } 12: 13: /* Show the monthly values and calculate the total */ 14: puts(" Month Value "); 15: for(i = 1, total = 0.0; i <= 12; i++){ 16: printf(" %5d %9.2f\n", i+1, sal[i]); 17: total += sal[i]; 18: } 19: 20: printf("Annual Total: %9.2f\n", total); 21: return 0; 22: }
An array's declaration and initialization can be done without indicating the array's number of elements.
In this case, the compiler will create an array with as many elements as there are initial values.
Arrays - Declaration Examples
int v[10]; /* Array with 10 non initiated elements */ int v[3] = {5, 10, 15}; /* Array with 3 automatically initiated elements with the values 5, 10 and 15 */ int v[10] = {5, 10, 15}; /* Array with 10 elements. The first three are initiated with the values 5, 10 and 15. The rest are initiated with 0. */ int v[] = {5, 10, 15}; /* Array with 3 elements initiated with the values 5, 10 and 15. Equivalent to int v[3] = {5, 10, 15}; */ int v[]; /* Incorrect declaration - causes a Compiling Error */
Some Notes About Arrays
- An array's elements are always stored in contiguous memory positions.
- The elements of an array declared with no initialization contain random values.
- An array's first element index is always 0 (ZERO).
- An array's indexes with n elements always vary between 0 and n-1.
- The existent value in a position of the array a can be obtained through the index in which that position is stored a[index].
- The compiler does not verify if the indexes used in an array are correct or not. A common example of an error in manipulating an array with n elements is using the index n (v[n]), which does not belong to the array and can cause severe problems, because we're altering memory that does not belong to us.
- The compiler doesn not verify if the indexes used in an array are correct or not with the array's declaration. I.E., we can declare an array with a deimension of 3 and reference (wrongly) superior or inferior positions, and the compiler won't indicates any errors.
- An array can be automatically initiated with the value set existent between curly brackets, following the = symbol. This can only be done when you're declaring the array.
- If the number of initial values is lower than the number of elements in the array, the remaining elements will be initiated with the value ZERO.
- You can declare an array without indicating the number of elements it will contain, as long as they're placed in its initial charge. In this case, the compiler automatically calculates the number of elements the array will contain.
- You can't declare arrays with no dimensions. If we don't know the pretended dimension, how will the compiler know the necessary space to hold it in?
Passing Arrays to Functions
Let's make a supposition out of the following declarations:
int v[10];
int x[20];
Let's suppose, then, that our intention was to initiate the arrays v and x with the value ZERO in all positions and that we pretended to utilize a function to execute such an initialization.
Since the arrays have different dimensions, we'll have to eventually define two distinct functions (one for 10 integer arrays, another for 20 integer arrays).
Let's, then, write the code for each of them:
void inic1(int s[10]){ int i; for(i = 0; i < 10; i++) s[i] = 0; } void inic2(int s[20]){ int i; for(i = 0; i < 20; i++) s[i] = 0; }
Now we have the code for each function.
Invoking these functions on main should be done like this:
int main(){ int v[10]; inic1(v); /* Initiate the array v using the function inic1 */ inic2(x); /* Initiate the array x using the function inic2 */ return 0; }
Notice that if we send a 10 integer array to the function inic1, it should have a parameter of the same type as the variable.
However, the two initialization functions can be substituted by a single one that initiates any integer array with the value 0. This is possible because, in C, what matters is not the size of the array that is passed to a function, but yes its element type, and, in that case, both arrays are composed of integers.
It is possible to write a function that initiates any integer array. However (because of the previous note), we have to indicate the number of elements the array contains.
prog0603.c1: #include <stdio.h> 2: 3: int void inic(int s[], int n){ 4: int i; 5: for(i = 0; i < n; i++) 6: s[i] = 0; 7: } 8: 9: int main(){ 10: int v[10]; 11: int x[20]; 12: 13: inic(v, 10); 14: inic(x, 20); 15: return 0; 16: }
Notice, now, that the function void inic(int s[], int n) receives an integer array (without indicating its dimension) and an integer that indicates what's the number of elements to initiate.
This way, its possible to indicate an array of integers (whatever is the dimension with which it was declared), because we do not indicate in the function parameter how many elements the array contains.
Constants
Writing programs should be done in a way that a small change won't cause big transformations in its code.
Let's, then, suppose the following program:
prog0604.c1: #include <stdio.h> 2: 3: void inic(int s[]){ 4: int i; 5: for(i = 0; i < 10; i++) 6: s[i] = 0; 7: } 8: 9: int main(){ 10: int v[10], i; 11: 12: inic(v); 13: 14: for(i = 0; i < 10; i++) 15: v[i] = i; 16: 17: for(i = 10 - 1; i >= 0; i--) 18: printf("%d\n", v[i]); 19: return 0; 20: }
This is a program that works for an array with 10 elements. However, if its alteration is extremely urgent, in order for it to be used not with 10, but with 25 integers, the solution is to alter all occurrences of the number 10 for number 25.
5: for(i = 0; i < 10; i++) 10: int v[10], i; 14: for(i = 0; i < 10; i++) 17: for(i = 10 - 1; i >= 0; i--)
Since it's a small program, the effort won't be much, but imagine if it was a program with thousands of lines of code.
In this case, we could use a good editor and automaticly alter all occurrences of 10 for 25 not much effort.
However, some unwanted effects could occur. What would happen to the following strip of code:
if(grade >= 10) printf("Approved"); else printf("Disapproved");
Most properly would turn into:
if(grade >= 25) printf("Approved"); else printf("Disapproved");
This would increase, in a significant way, the number of disapproved students in that subject.
The solution is in using constants that, once altered, propague their new value through all occurrences.
Defining Constants
A constant is nothing more than a name correspondent to a fixed value (cannot be altered throughout the execution of a program).
These should be defined, normally, outside of any function, in order to make them visible to the entire program.
Defining constants can be done in two distinct ways:
- Through the reserved keyword const;
const type symbol = value; - Through the pre-processing directive #define
#define symbol value
Examples:
prog0605.c1: #include <stdio.h> 2: const int num = 10; 3: 4: void inic(int s[]){ 5: int i; 6: for(i = 0; i < num; i++) 7: s[i] = 0; 8: } 9: 10: int main(){ 11: int v[num], i; 12: 13: inic(v); 14: for(i = 0; i < num; i++) 15: v[i] = i; 16: for(i = num - 1; i >= 0; i--) 17: printf("%d\n", v[i]); 18: return 0; 19: }
prog0606.c
1: #include <stdio.h> 2: #define NUM 10 /* No Semi-Colon */ 3: 4: void inic(int s[]){ 5: int i; 6: for(i = 0; i < NUM; i++) 7: s[i] = 0; 8: } 9: 10: int main(){ 11: int v[NUM], i; 12: 13: inic(v); 14: for(i = 0; i < NUM; i++) 15: v[i] = i; 16: for(i = NUM - 1; i >= 0; i--) 17: printf("%d\n", v[i]); 18: return 0; 19: }
If we want to alter our array size from 10 to 25, we'll only need to alter the line where the constant is defined, independently of its number of occurrences inside of the program.
Differences between const and #define
- A constant defined with
Matrixes and Multidimensional Arrays
Up until this moment, we could only work with unidimensional arrays. Let's, now, learn how to use arrays with more than one dimension.
Declaring unidimensional arrays is nothing more than a particular case in declaring arrays with any number of dimensions.
Declaring an array with n dimensions is done through the following syntax:
As an example, let's observe how we could implement the famous game Tic Tac Toe, through an array as a game board.
X | O | |
X | ||
O |
As you can see, we need a two dimensional array (vulgarly called a Matrix).
Each square will have an X, an O or a blank space. Thus, it's a character array.
The declaration should be done like this:
#define DIM 3 ........ char tic[DIM][DIM]; /* 3 Rows, each with 3 Positions
In the previous case, what we've declared is not a matrix, but yes an array with three positions tic[3] where, in each of those positions, there's a three character array.
tic[0] | ==> | X | O | |
tic[1] | ==> | X | ||
tic[2] | ==> | O |
Thus:
- tic - it's a character array with two dimensions 3 x 3.
- tic[i] - it's a three character array.
- tic[i][j] - character stored in the i row and j column of the tic array.
If we declare an array
int v[3][4];
- v - it's a three element array (each element is a 4 integer array),
- v[i] - it's a four integer array.
- v[i][j] - integer stored in the i,j position of the v array.
Multidimensional Arrays: Automatic Initialization
The initialization must be done in the same way as with unidimensional arrays.
Let's suppose, then, that we wanted to start off our game of tic tac toe with blank spaces on all positions.
char tic[DIM][DIM] = {' ',' ',' ',' ',' ',' ',' ',' ',' '};
Although this is the traditional way to initialize the array, it is not correct, because tic is not a 12 element array - it's an array made out of three arrays.
The initializing of a one dimensional array with three spaces is done like this:
char v[3] = {' ',' ',' '};
Now, since the initialization of an array is done through curly brackets, the initialization of the tic array should be done like this:
char tic[DIM][DIM] = {.......};
Since the tic array is a three element array (each one is also an array), then we'll have to place only three elements between curly brackets:
char tic[DIM][DIM] = { , , };
Our current problem is knowing what elements to place.
Question: What corresponds to tic[0]?
Answer: tic[0] is a 3 character array?
Question: How do you initiate a 3 character array with blank spaces?
Answer: By placing the respecive characters between curly brackets {' ',' ',' '}?
Now we know how to initiate the first element of the array.
char tic[DIM][DIM] = {{' ',' ',' '}, , };
The others are initiated in the same way.
Thus, the initialization of the tic array can be done in two distinct ways:
char tic[DIM][DIM] = {' ',' ',' ',' ',' ',' ',' ',' ',' '};
or
char tic[DIM][DIM] = {{' ',' ',' '},{' ',' ',' '},{' ',' ',' '}};
Example: Declare and initiate the integer array v[2][4] with any integer values you want.
int v[2][4] = {{1,2,3,4},{5,6,7,8}};
Exercise:
Write a program that sets the game board for Tic-Tac-Toe as follows, after being initiated with blank spaces during its declaration.
X | O | |
X | ||
O |
Next, print it out on-screen.
test.c1: #include <stdio.h> 2: #define DIM 3 3: 4: int main(){ 5: char tic[DIM][DIM] = {{' ',' ',' '},{' ',' ',' '},{' ',' ',' '}}; 6: int i, j; 7: 8: tic[0][0] = 'X'; 9: tic[1][1] = 'X'; 10: tic[0][2] = 'O'; 11: tic[2][2] = 'O'; 12: 13: for(i = 0; i < DIM; i++){ 14: for(j = 0; j < DIM; j++) 15: printf("%c %c", tic[i][j], j == DIM - 1 ? ' ' : '|'); 16: if(i != DIM - 1) printf("\n--------\n"); 17: } 18: return 0; 19: }
$ ./test X | |O -------- |X | -------- | |O
In the automatic initialization of an array, you can only omit a numeric value for the dimension furthest on the left, the respective value being calculated by the compiler.
chat tic[][] = {{' ',' ',' '},{' ',' ',' '},{' ',' ',' '}}; /* Error */ char tic[][3] = {{' ',' ',' '},{' ',' ',' '},{' ',' ',' '}};
It's equivalent to
char tic [3][3] = {{' ',' ',' '},{' ',' ',' '},{' ',' ',' '}};
Multidimensional Arrays: Passing to Functions
The passage of polidimensional arrays to a function is done by indicating the number of elements of each of the n-1 dimensions furthest to the right on the function header. Only the dimension furthest to the left can be omitted, placing only [] or an asterisk *.
It's, however, common to place all array dimensions on-code.