Lesson 7 - Arrays in the C language
In the previous exercise, Solved tasks for C lesson 6, we've practiced our knowledge from previous lessons.
Lesson highlights
Are you looking for a quick reference on arrays in C instead of a thorough-full lesson? Here it is:
Shortened initialization of an array:
int numbers[] = {2, 3, 5, 7};
Writing 1
at the position [0]
:
numbers[0] = 1;
Reading the value (now 1
) at the position
[0]
:
{C_CONSOLE}
int numbers[] = {2, 3, 5, 7};
numbers[0] = 1;
printf("%d", numbers[0]);
{/C_CONSOLE}
Printing the whole array:
{C_CONSOLE}
int numbers[] = {2, 3, 5, 7};
numbers[0] = 1;
int i;
for (i = 0; i < 4; i++)
printf("%d ", numbers[i]);
{/C_CONSOLE}
Creating an empty array and generating the
values using a for
loop:
#include <stdio.h>
#include <stdlib.h>
#define COUNT 16
int main(int argc, char** argv) {
// Creates an array
int numbers[COUNT];
int i;
for (i = 0; i < COUNT; i++)
numbers[i] = i + 1;
for (i = 0; i < COUNT; i++)
printf("%d ", numbers[i]);
return (EXIT_SUCCESS);
}
Determining the size of a statically allocated array:
{C_CONSOLE}
int numbers[10]; // Creates a new array of 10 integers
printf("The array size is: %d", sizeof(numbers)/sizeof(int));
{/C_CONSOLE}
Sorting an array using qsort()
:
#include <stdio.h>
#include <stdlib.h>
int compare(const void * a, const void * b)
{
return (*(int*)a - *(int*)b);
}
int main(void)
{
int numbers[] = {15, 8, 3, 10, 9, 2, 2};
qsort(numbers, 7, sizeof(int), compare);
int i;
for (i = 0; i < 7; i++)
{
printf("%d ", numbers[i]);
}
return (EXIT_SUCCESS);
}
Would you like to learn more? A complete lesson on this topic follows.
In the previous tutorial, Solved tasks for C lesson 6, we introduced loops in the C language. In today's lesson, we're going to introduce you all to the array data structure and show you what it's capable of accomplishing.
Array
Imagine that you want to store some information about multiple items, e.g.
you want to keep 10
numbers in a memory, each of the fields of a
checkerboard or names of 50
users. Perhaps you realize that there
must be an easier way than to start typing variables like user1
,
user2
... up until user50
. Despite the fact that there
may be 1000 of them. How would go about searching for something in there?
Definitely not like that!
If we need to store a larger amount of variables of the same
type, we can solve this problem using an array. We can imagine it as a
row of boxes, each of them containing one item. The boxes are numbered by
indexes, the first one has index 0
.
(We see an array of 8 numbers on this picture)
Programming languages are very different in the way they work with arrays. In the lower, compiled languages, to which the C language belongs, we have to specify a fixed size for an array, and we are unable to change it during run-time. Meaning that it is not possible to add more "boxes" to an existing array, so we have to keep this in mind at all times. The C language also allows us to declare dynamically allocated arrays or use things like linked lists to work around this limitation. However, this requires rather complex knowledge and therefore we'll get to it later. On the other hand, some interpreted languages allow us to declare an array of any size and to change this size during run-time. An example of such a language is PHP.
We use loops for mass manipulation of array items.
We declare an array as an ordinary variable and then add brackets with the number of its items after the variable name:
int numbers[10];
Numbers
is obviously the name of our variable. Now, there is an
array
of the size of 10
int
s in the
numbers
variable. We've just created the array and the
operating system has allocated memory for it which could've been used by other
applications before. Therefore, we cannot be sure that it has all been zeroed
out. It could be full of completely random numbers.
We access array items using the brackets as well. Let's access the first
index (the index 0
) and store the number 1
into
it:
int numbers[10]; numbers[0] = 1;
Filling an array manually like this would be too laborious. We'll use a loop
and fill the array with numbers from 1
to 10
. We'll
use the for
loop to do so:
int numbers[10]; int i; for (i = 0; i < 10; i++) { numbers[i] = i + 1; }
Note: We store i + 1
to the array because i
goes from 0
and we want to store numbers from
1
.
To write the array to the console, we can add the following code to the end of our program:
{C_CONSOLE}
int numbers[10];
int i;
for (i = 0; i < 10; i++)
{
numbers[i] = i + 1;
}
for (i = 0; i < 10; i++)
{
printf("%d ", numbers[i]);
}
{/C_CONSOLE}
The result:
Console application
1 2 3 4 5 6 7 8 9 10
Of course, we can initialize an array manually as well and also without the need to initialize each index gradually. We can specify array items into curly brackets and separate them by commas:
int numbers[] = {15, 8, 3, 10, 9, 2, 2};
Notice that we don't need to specify the array size, the compiler will determine it from the number of elements in the curly brackets.
Arrays are often used for storing intermediate results which we can then use further in the program. If we need to use something 10 times, we won't compute it 10x, but only once. Then, we'll store the results in an array and retrieve them later.
Constants
Since we've to specify the array length in a source code and we usually use
this value at multiple places of the program, it'd come in handy to have the
value stored somewhere. There's nothing worse than to decide we want an array
only 10
items long instead of 15
items long and forget
to change some of the numbers 15
in the code, e.g. in the loop
which is printing the array to the console. Therefore, we often store array
sizes into constants.
A constant is a value of any type which cannot be changed. We can understand
it as a read-only variable. We use constants for storing values which don't
change at the run-time but we as programmers want to keep an option to change
them easily in the code in case of a change needed. We define constants using
the #define
directive, there's a convention to name them with
UPPERCASE_NAMES
. We specify the value of a constant just after its
name, separated by a space or a tab character, there is no "="
. We
define them straight after the #include
directives. Let's show the
complete source code of the program above using a constant for specifying the
array length:
#include <stdio.h>
#include <stdlib.h>
#define COUNT 10
int main(int argc, char** argv) {
// Creates an array
int numbers[COUNT];
// Fills the array
int i;
for (i = 0; i < COUNT; i++)
{
numbers[i] = i + 1;
}
// Prints the array
for (i = 0; i < COUNT; i++)
{
printf("%d ", numbers[i]);
}
return (EXIT_SUCCESS);
}
Note: Commands which start with #
are not the compiler
commands, but commands for the preprocessor. It's a program which gets to the
source code first and inserts some code snippets there to make it easier for the
compiler. In our case, the preprocessor inserts the definitions of the functions
from the stdio.h
and stdlib.h
system libraries and
then replaces every occurrence of COUNT
with the value
10
. More precisely, the constant is a macro and there are several
more things which the preprocessor can do. However, we won't bother with them
now.
Array bounds
Beware! The C language doesn't make verify that we're working within the array bounds. It was made this way to maintain the high program speed. Meaning that we're able to do things like store data at the 15th index even when an array is only 10 indexes long. An array is stored as a block of bytes in memory and the C language computes the address at which it'll write based on a given index. In other words, we're able to write at an index which is too high, to a memory which it doesn't belong to. We can corrupt other data stored by our application through an array which isn't related to our current array at all. It's generally very hard to spot such errors, so we're better off avoiding them.
An array of a length specified during run-time
The C99 standard allows us to declare so-called VLA (Variable Length Array) which are arrays of a length specified after the program has begun running. Meaning that code like this will work:
int size; printf("Enter the array size and I'll create it: "); scanf("%d", &size); int numbers[size];
Although it's the current standard, it's possible that the code won't work on all compilers. We can achieve a similar functionality with dynamic arrays which we'll get familiar with in the next course. Even with VLAs, once we create an array, its size can't be changed.
Array size
The arrays which we've created were allocated statically. We're able to determine the size of array of this kind using the following approach:
{C_CONSOLE}
int numbers[10]; // Creates a new array of 10 integers
printf("The array size is: %d", sizeof(numbers)/sizeof(int));
{/C_CONSOLE}
We simply determine how many bytes the whole array occupies and then divide this number by the size of the data type of a single item. This way, we get the number of items within it.
The result:
Console application
The array size is: 10
This approach cannot be used with dynamic arrays and texts, so it's better not to stick to it.
Sorting an array
We often need to sort items in an array. For example, when we need to get the
highest/lowest wage, number of points, costs, the number of pieces, etc.
Although this a task was often given to students to train for algorithms, the
standard C library provides the qsort()
function for these
purposes. Calling it is a bit complicated, we're going to describe it rather
intuitively. Nothing is stopping us from using the function now (you'll
understand it better further along in the courses).
#include <stdio.h>
#include <stdlib.h>
int compare(const void * a, const void * b)
{
return (*(int*)a - *(int*)b);
}
int main(void)
{
int numbers[] = {15, 8, 3, 10, 9, 2, 2};
qsort(numbers, 7, sizeof(int), compare);
int i;
for (i = 0; i < 7; i++)
{
printf("%d ", numbers[i]);
}
return (EXIT_SUCCESS);
}
Aside from the main()
function, we also have a comparing
function which defines how to compare two array items. This is because
qsort()
internally compares pairs of array items, and is able to
sort the array in doing so. Don't mind the asterisk syntax, it's only about
returning a - b
, which would be positive for a > b
,
zero if a equals b and negative for a < b
. qsort()
will be able to compare items according to this value. If we wanted to sort the
array as descending, we'd enter b - a
.
Creating the array should be clear to you all. When calling
qsort()
, it's necessary to specify an array, and also the size of a
single element in bytes, and the comparing function. The array items are now
sorted. However, we'll write them to the console just to make sure.
The result:
Console application
2 2 3 8 9 10 15
That's enough for today, you can play with arrays for a while if you'd like. In the next lesson, Solved tasks for C lesson 7, we'll finally learn how to work with texts in the C programming language
In the following exercise, Solved tasks for C lesson 7, we're gonna practice our knowledge from previous lessons.