C++ Tutorial 6: Data Types, Pointers, Data Structs, Arrays, ....

Welcome back to C++ Tutorials. This Tutorial has taken a while but it will be worth it. It is also very very long.

First off we will be covering a lot. So lets begin:

First:

chars. I really haven't talked about them for a reason. Chars are basically out done by strings (which uses chars by the way ;) ).

a char looks like this:

char character = 'S';

the char type can only contain one character. But there are exceptions. These exceptions are pointers and arrays.

Pointers and arrays are similar. Arrays and pointers are generally used in the same way but pointers have a few extra functions.

Arrays:

arrays look like this:

type[number] name;

type is the type for the array. [] are used to say this is an array (NOTICE NOT ALL CLASSES CAN BE USED AS AN ARRAY! WE WILL TALK ABOUT THIS WHEN WE GET TO OPERATOR OVERLOADING!). number is the number of "blocks". name is the name.

arrays in memory look like this (well actually no but to us it seems like this even though it isn't ;) ):

say we have this:

char[2] twochars;

it will look like this:

__________________
char|0|value
char|1|value
char|2|value
------------------------------

wait a second! there are 3 values there?

yes, in c++ and in most languages 0 is also there. so when you write:

char [1] name;

you are actually creating 2 chars. confusing isn't it? you'll get used to it though.

Ok now pointers. I generally use pointers instead of arrays but there are some real big dangers with pointers!

The dangers are (this will not happen if you are not in ring 0 (more on this later)):

you can corrupt your hard driver
you can corrupt your os
you can corrupt your memory
you can cause triple faults or aka cold reboot your machine
you can severely damage a machine and make it unusable.

Now those are some pretty serious things aren't they?

Well don't worry after I'm done teaching you you will know how to avoid these things and make sure these things never happen to you.

Pointers:

Pointers are references to memory addresses. Anything beyond this won't make any sense unless I show you what a pointer is ;)

char* pointer;

is that an asterisk ( * )? yes, that is how you define a pointer. now these pointers can be applied to ANY class unlike arrays (because they are references to memory ;) )

so:

to use a pointer you can use it normally but:

you know how a char can only hold one character?

well

char* pointer;

pointer = "This is way more than one character";

now isn't that more than one character? yes it is but with pointers you can do things like that. Because pointers are references to memory if you do that it will write the T to the value of pointer then it will write the other characters to memoryaddressofpointer + placeofcharacterinstring so:

say char * pointer is a reference to 0x1. the T of this is written to 0x1. the h is written to 0x2. the i to 0x3. etc.

So pointers are used to define memory and in the case of char* pointer every address beyond 0x1 that you use is considered a char. So this is also valid:

char* pointer;
pointer = "this";
cout << pointer[3]; //will output s

you can use pointers with the same syntax of arrays [number]. so you can get individual elements in a pointer.

Now for the danagers:

char* pointer = "this";
cout << pointer[5];

YOU CAN DO THIS BUT you can do a couple things one if that address is used by another part of your program you will output the memory in there. Now this also measn that you can do this:

char* pointer = "this";
pointer[5] = 's';

now here is another thing. when you use "this" you are actually writing this ( is one character) is null and says that this is the end of the array. So when you do 5 you will be writing outside of the making it look like this when you output it:

thiss ( may not be there depending on how cout is written)

Now I said something earlier about arrays and pointers which is not true. I said this because it is easier for you to understand but now that you understand both pointers and arrays I can tell you and it will make sense. Arrays are actually pointers BUT they have a fixed portion of memory unlike pointers which do not.

That covers pointers and arrays.

Now we will move on to Dynamic Memory:

This is going to be a short but important section. Up til now all our programs have only used the memory we have defined for them with the types so char is 1 byte. and so on.

All this was determined BEFORE we ran our program. But, what if we need a variable amount of memory that can only be determined during runtime? For example, in the case that we need some user input to determine the necessary amount of memory space.

The answer is dynamic memory, for which C++ integrates the operators new and delete.

In order to request memory we use new (new[]) and to get release that memory (from our dirty clutches :P ) we use delete (delete[]). So

pointer = new type
pointer = new type [number_of_elements]

int* pointer = new int[5]

in this case the OS assigns memory to our program (or OS). It assigns 5 elements of type int and returns a pointer to the first one.

Now another feature of pointers. To access the first element we can either use pointer[0] or *pointer; the asterisk is used to convert the pointer (a memory address) into the value of the memory address.

But now how do we check if we assigned those 5 ints? C++ has two was of doing it:

exceptions:

Using this method an exception of type bad_alloc is thrown when the allocation fails. Exceptions are a powerful C++ feature explained later in these tutorials. But for now you should know that if this exception is thrown and it is not handled by a specific handler, the program execution is terminated.

This exception method is the default method used by new, and is the one used in a declaration like:

pointer = new int[5]; //if fails exception is thrown and program is terminated!
The other method is known as nothrow, and what happens when it 
is used is that when a memory allocation fails, instead of throwing a bad_alloc
exception or terminating the program, the pointer returned by new
is a null pointer, and the program continues its execution.



This method can be specified by using a special object called nothrow,

pointer = new (nothrow) int [5];

so to detect this we would use:

pointer = new (nothrow) int[5];
if (pointer == NULL)
{
//do something since it failed
}

Since the necessity of dynamic memory is usually limited to specific moments within a program, once it is no longer needed it should be freed so that the memory becomes available again for other requests of dynamic memory. This is the purpose of the operator delete (delete[]).

We use this by:

delete pointer;
or
delete[] pointer;

we should use the first one for memory allocated to one element and the second one for many elements. (two or more)

One last thing on new and delete:

for (int i=0;i {
int pointer2 = new int[i];
delete[] pointer2;
}

now that is a pretty pointless example but the key thing is that you can do this:

type name = new type[variable];

you can use variables in [] and in replace of anything that requires a specific type as long as that variable matches the type of the one required.

There is one more crucial thing before we move on to the next tutorial:

structs

structs are classes with a different name but you don't use constructors and destructors (well you can but then it'd be a class).

The reason you use structs is for things like:

struct star {
Vector2f position;
int brightness;
int timetilldeath;
} star;

this is an object. Generally if you have an object but the object doesn't require functions you should use a struct. a struct is short for a structure.
So. To end this tutorial I've made this sample code:

Code

struct star {
    int position;
    int brightness;
    int timetilldeath;
}

int main()
{
    while (true)
    {
        star* stars = new (nothrow) star[100];
        if (stars != NULL)
        {
            for (int i = 0;i < 100;i++)
            {
                stars[i].position = rand()%100;
                stars[i].brightness = 0;
                stars[i].timetilldeath = 1;
                for (int b=0;b                 {
                    cout << " ";
                }
                cout << "*" << endl;
            }
        }
        else
        {
            cout << "Yea out of memory exception!!!! Notice we didn't free all that memory? try fixing this program to do such a thing";
            return 1;
        }
    }
}




hmm... fix this program and also note one new function before we move on:

rand()%100;

is that rand() then a percent sign then 100?

yes what that does is this (First in C++ the percent sign (%) is referred to as modulus) it chooses a random number between 0 and 99 (remember number = number - 1). Unfortunately this will give us the same number unless we first seed it. So to do that we use:

srand(rand()%1000000000000000);

To make sure you understand this long tutorial completely I want you to fix the program above to seed the rand and also fix it so that it will not get an out of memory exception! Once you do that you can move on to the next tutorial:

Polymorphism
Templates
Exceptions
Type Casting
Pre-Processor Directives
Input and Output with Files

After that you will know everything to develop on FrostOS but I strongly recommend completing all the tutorials. (After the next one it is writing programs to do certain tasks (Tutorials 8 - 20) )

No comments:

Post a Comment