Arrays in C
Arrays
Arrays are a collection of the same datatype which can be acessed through the same variable.This variable can have one or more slots and each member can be acessed through each slot number. We will call our slots subscripts. This is how we declare an array:
//int array[subscript];
int a[3];
a[0] = 8;
a[1] = 9;
a[2] = 10;
printf("%d %d %d\n", a[0], a[1], a[2]);
You may also declare in the same line:
int a[3] = {8, 9, 10};
You may omit an element in declaration:
int a[3] = {8, 9};
Or my favorite declaration, declaring its size automatically. All you have to do is omit the size in the subscript:
int a[] = {2, 4, 6, 8};
Notice arrays start at element 0, so the position your element really occupies in the array is its index + 1. Its possible to not declare an element to an array index, and in this case the element will contain dirty value like an not initialized pointer:
int a[3];
a[0] = 8;
a[1] = 9;
printf("Element 0: %d\nElement 1: %d\nElement2: %d", a[0], a[1], a[2]);
When you declare an array, the variable "a" we named the pointer will be like a pointer to the memory of the first element of the array, so we could have written the code above dereferencing it, like this:
int a[3];
a[0] = 8;
a[1] = 9;
//acessing element [0] through pointer now
printf("Element 0: %d\nElement 1: %d\nElement2: %d", *a, a[1], a[2]);
And also get the next elements performing pointer arithmetic:
int a[3];
a[0] = 8;
a[1] = 9;
printf("Element 0: %d\nElement 1: %d\nElement2: %d\n", *a, *a+1, *a+2);
Since you can retrieve the value of the array through pointer arithmetic, you must be careful whenever you try this technique, since you can reach areas beyond the array depending on the arithmetic you perform. To make sure you will not perform an invalid arithmetic which may even crash your program, you can use the sizeof instruction we have seen in last thread:
int a[4] = { 12, 14, 16, 18 };
int a_size = sizeof(a) / sizeof(a[0]);
printf("Array size: %d\n", a_size);
Then you can iterate it trough a loop performing pointer arithmetic:
int a[4] = { 12, 14, 16, 18 };
int a_size = sizeof(a) / sizeof(a[0]);
printf("Array size: %d\n", a_size);
for (int i = 0; i < a_size; i++) {
printf("Array element %d is %d \n", i, *(a+i));
}
Remember you must dereference the operation, thats why we placed a parenthesis in *(a+i). If you forget doing so, the loop will dereference the value of a, which points to the first element of the array, which is 12, and add 1 to this element performing a normal arithmetic on it, not a pointer arithmetic.
As you have seen, pointer arithmetic is a valid operation in loops, but it wont hurt to remind you can iterate trough the loop using its index also:
for (int i = 0; i < a_size; i++) {
printf("Array element %d is %d \n", i, a[i]);
}
Since arrays and pointers are so close one another, its no surprise you can declare an array of pointers. Any change you perform in the array index of an array of pointers, will be reflected on the original variable:
int a = 10, b = 15, c = 20;
int ar[3] = { a, b, c };
ar[0] = 17;
ar[1] = 23;
ar[2] = 28;
printf("value of a: %d, b: %d, c: %d\n", a, b, c);
int *par[3] = {&a, &b, &c};
*par[0] = 17;
*par[1] = 23;
*par[2] = 28;
printf("value of a is now: %d, b is now: %d, c is now: %d\n", a, b, c);
Pointers and arrays
Altough both pointers and arrays can use indirection, they are not the same thing. Arrays cant modify the place they point to and declaring arrays will reserve a space in memory for the amount of elemnts of the array, while a pointer will only set memory for himself. Also, the act of only declaring an array without initializing it will already set a a memory adress for it, but if you declare a pointer without initializing it, no memory adress will be set. So you can try to acess an uninitialized array:
int a[5];
printf("%d\n", a[3]);
But this expression will be invalid:
int *p;
printf("%d\n", *p);
Strings and arrays
In C strings are arrays of char datatypes. Char is the smallest datatype in C and has the size of only 1 byte. After filling your string with bytes you then have to terminate with a null character. But its not that complicated as my explanation. Lets take a look how we do it since an image is worth a thousands words:
char word[] = {'m','e','s','s','a','g','e',0};
Now, to print we only have to change the format to %s:
char word[] = { 'm','e','s','s','a','g','e',0 };
printf("%s\n", word);
In the network, many messages will be delivered as such, in a stream of bytes like the array, and sometimes even debugging you gonna have to pay attention to it.
Multidimensional Arrays
So far, we have seen how to work with unidimensional arrays, but arrays can contain other arrays, as long as they are the same type. Lets see how to declare them:
int a2[2][3];
This declaration creates a multidimensional array with 6 elements of the int datatype which is the product of 2 * 3. But instead of seeing it as an unidimensional array of 6 elements, they will be stored as 2 arrays, containing 3 elements each:
int a2[2][3];
a2[0][0] = 2;
a2[0][1] = 4;
a2[0][2] = 6;
a2[1][0] = 8;
a2[1][1] = 10;
a2[1][2] = 12;
int *pa2 = &a2;
for (int i = 0; i < 6; i++) {
printf("element %d is %d\n", i, *(pa2 + i));
}
After initializing, all we have to do is loop trought all elements to check how they are stored one after another, as if they were a unidimensional array (but if you are using pointers, dont forget to use parenthesis, since * operator has higher precedence, or else it will dereference the value of whats inside the memory area of the pointer and ad 1 to it every iteration, like we explained before).
Now, if we wanna output the result as a bidimensional array, all we have to do is a nested for loop printing the array index:
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("[%d][%d] is %d\n", i, j, a2[i][j]);
}
}
But when applying indirection, we must remember we will be dereferencing a pointer inside a pointer, so we must apply as many indirections as the multidimensional size of the array, the performing the pointer arithmetic. For example, we could acess element a2[0][1] only though indirection:
printf("a2[0][1] is %d\n", ((a2 + 0) + 1)); //(Remember the parenthesis and the pointer precedence!!!)
Or, perhaps you feel more comfortable by mixing dereference with a subscript:
printf("a2[0][1] is %d\n", *(a2[0] + 1));
Multidimensional arrays can be initialized with all its elements in one line:
int a2[2][3] = {2, 4, 6, 8, 10, 12};
But i personally dont like very much this approach. Imagine you have a big array and someone would have to read your code and visualize this multidimensional array. He would probably be able to do so only through code. Thats why I’d rather this declaration and inicialization, where you can see clearly there are arrays inside arrays:
int a2[2][3] = { {2, 4, 6}, {8, 10, 12 } };
Better isnt? Now we can see clearly 2 arrays inside a array, with 3 elements each.
When You are automatically declaring an array size, you cant do the same with multidimensional arrays as you did with one dimensional arrays. With multidimensional arrays only the dimension of the first subscript can be implied:
int a2[][3] = { {2, 4, 6}, {8, 10, 12 } };
So the following declaration and inicialization would be illegal:
int a2[][] = { {2, 4, 6}, {8, 10, 12 } };
Our arrays can also contain pointers so we can declare arrays of pointers, like we said before:
int x = 7;
int y = 17;
int z = 27;
int *ap[3] = { &x, &y, &z };
The declaration looks like the previous statements we have seen when we want to return a value from a multidimensional array, but only on the surface (thats why i told you to remember the parenthesis and precedence always!). Those can easily point to other arrays, which can contain arrays and such. You can do it for example, if you want to pass a variable number or arrays to a function to be processed and have their original value modified. Lets check a silly example on my mind:
int main()
{
int x[2] = {3, 6};
int y[3] = {4, 7, 10};
int z[4] = {10, 13, 14, 18};
int *ap[3] = { &x, &y, &z };
printf("x[0]: %d, x[1]: %d\n", x[0], x[1]);
f1(*ap);
printf("x[0]: %d, x[1]: %d\n", x[0], x[1]);
return 0;
}
int f1(int *ap) {
//make any processment here
*(ap+0) = 199;
*(ap + 1) = 344;
return 0;
}
Now you can only pass a simple pointer, or a constant pointer instead of big arrays, and thousands of big arrays, a pointer to arrays of pointers, and a variety of possibilities :)