Arrays -> as pointers and expressions

Arrays -> as pointers and expressions

Whenever we need to define an object that holds a series of other objects, the basic data type for this would be an array or vector. The contents of an array/vector should be of the same data type, meaning we can’t have an array of both integers and booleans, or an array of strings, and long. An example for defining an array of scores would be:

int scores[] = {0,1,2,3,4,5,2,1,5,6};

Here, scores is an array object holding 10 integer objects. In terms of memory, there are a couple of other things to know about this array. Typically, an int has a size of 4 bytes within the memory. scores is an array of 10 integers. This means that the score object has a total size of 4 * no_of_items in container -> (4 * 10 ) = 40 bytes. In memory, this scores object would occupy a space of 40 bytes. An array of 2 integers would have a memory size of 4bytes * 2 = 8 bytes.

When you need to access one of the objects within the array, we can use a subscript operator “[]” to achieve this. The subscript operator takes in an integer, which represents the position of the object within the array. The initial position of any element within the array has a position of 0, meaning to access the 4th element in an array, the position would be 3. An example:

int scores = {15,33,69,87,125};
//to access the 125, we would use the position at where 125 lies in the array
int value = scores[4]; // value is now 125

But what happens when we refer to an array position that is beyond the size of this array, say 7? We would expect an error to occur, or the program to crash (ArrayIndexOfOutBoundsException). The truth is this might not happen. Imagine we tried printing out the element of the scores array at position 7(Remember that this position does not exist since score has a size of 6), it is very likely we would get a value returned, but this value actually refers to a different kind of thing within the memory. An example:

int list[] = {10,24,56};
cout << list[4] <<endl; //print the 5th item in the array. (-45752158 was my result on my computer)

The fifth item here does not exist, but we could get a random value of something else. We can understand why this happens if we look at this at a memory point of view. Recall that an int has a size of 4bytes, and an array of ints would have a size equal to the size of each items(4 bytes) multiplied by the total number of items in the array. These items in the array occupy addresses in memory next to each other.

Presentation1.png

So basically, the array object spans from the first container element address, to the last container element address in memory. There could be an object elsewhere within the program that occupies the memory address just immediately after the end of the array, so when we use a subscript value greater than the size of an array to get an element, it is likely we get a reference to another object in memory that we totally have no idea about.

Arrays and expressions.

Consider an example where we have an array of characters,

char values[] = {‘A’, ‘B’, ‘C’};

Any time we try to use the values object to do some form of operation, rather than getting the object itself, we would get a pointer to the first element within the array. So, values here is basically a pointer to ‘A’, not the entire array. It wouldn’t make sense when we pass entire arrays as values or parameters in a program, because these kinds of objects usually hold a list of other objects, sometimes this list can be fairly large and using the list as a whole anywhere in our program could lead to a huge amount of memory usage. With this idea, now would be a good time to explain what really happens when we subscript an array.

char values[] = {‘A’, ‘B’, ‘C’};
char oneValue = values[1]; //oneValue now holds ‘B’

Remember, using the values object is usually a pointer to the first element within an array, so here’s a breakdown of what happens with this operation.

char* temp = values + 1; // values is a pointer, adding one advances the pointer by one space. temp is now a pointer to the next value of the array.
char oneValue = *temp; //dereferencing the pointer yields the value in that array

Essentially, subscripting an array usually means advancing the pointer of the first element in the array by a number of ‘n’ spaces, which is later dereferenced and yields the value the pointer points to.

This same thing happens when we pass an array as a parameter to a function. Suppose we have a function that adds two numbers x, y together. We would write this as:

void addNumbers(int x, int y){
    cout << (x+y) <<endl;
}

int value1 = 4;
int value2 = 7;
addNumbers(value1,value2); //calling add numbers passes copies of x and y into the function

But with functions that accept arrays as a parameters, thing work a little different from usual. Imagine we need this function in our program to return an item at a particular index in an array;

int doStuff(int someValues[], int index){
    return someValues[index];
}

This would still work, but not in the way we expect because, someValues won’t be an array. Rather, it would be a pointer to the first element of the array passed to it. Then subscripting the pointer with an index would just advance the pointer by a number of spaces based on the value of the index. Which means we could just rewrite our function as;

int doStuff(int* someValues, int index){
    return someValues[index];
}

This approach is more efficient because rather than copying the whole array into another function that executes on top of the stack, we just get something that points to the first element, and we could advance the pointer by the desired amount of space needed.

If we need a function that loops through an array and prints all the values in it, we can just pass the pointer of the first element to the array, along with the size of the array to run the loop efficiently. Since there isn’t a member function for getting the size of an array, we can do this through diving the total memory size of the array by the memory suize of one element in that array. We can get the memory size of an object by calling the sizeof() function on that object. An example would be:

// function that prints all items in the array
int doStuff(int* someValues, int size){
    for(int i = 0; i<size; ++i){
    cout << someValues[i] << endl;
}
}

int scores[] = {0,2,3,5,5}; //an array of ints, each int has a size of 4 bytes
int sMemorySize = sizeof(scores); // size is 20 bytes
int pSize = sizeof(scores[0]); // we get the size of one element within the array, which yields 4 bytes
int size = sMemorySize/pSize; // this would be (20/5) which yields 5
doStuff(scores, size);

Interesting stuff this week:

  1. I couldn’t help it, so I had to go check up on what to expect from Unreal Engine. I took a brief look at some of the code syntax with C++ on the engine, because I learned that the C++ paradigm with Unreal is a bit different from vanilla C++. One of the differences being that multidimensional arrays can’t be used on the engine. I’m guessing we would use other container elements, like vectors that can contain other container elements, but I wouldn’t be surprised if I get hit with a caveat that says, “This isn’t considered as a best practice when working with the engine” lol.

  2. Speaking of best practices, I worked on a couple of utility functions with C++ (map, flat, reduce, etc). I would have created them as member functions on the vector class when extended, but I faced a couple of issues with using the list initialization methods on them. While looking up solutions for this, I realized that extending the vector class is considered a “bad practice” for reasons I’m yet to understand lol. Also, I didn’t quite get how to make the subclass inherit the list initialization from the vector object.

  3. Finally, bitwise operators! I got a basic idea on how powerful these operators can be, especially when working with colors. It’s funny how I’ve used these operators on other non-C projects (such as with Android flags), but had no idea that I was using them lol. I haven’t fully understood bitwise operators, but I’m getting there. And of course, a blog post about it :D. Meanwhile, I practiced the basic concepts of these operators by writing a HEX-to-RGB converter. Then I wrote a RGB-to-HEX converter as spice to keep it at 💯.