Home Page
The Basics

Classes
Abstract Data Types
Dynamic Memory Allocation
Operator Overloading
Lists
C++ Style Strings
Linked Lists
Stacks and Queues
Variable Data Types
Templates
Sorting

Arrays


Arrays are "packages" of information of the same type. In reading types in C and C++ we can use the "flip-flop" method. Start at the variable name and then go as far to the right then as far to the left as possible.

Example 1:    int ar[5];     // ar is an array of 5 integers
Example 2:    int ar[3][4];  // ar is an array of 3 arrays of 4 integers

following is an illustration of how  a two dimensional array is stored in memory:

 ar[0][1]1 234ar[0][3]
 ar[1][1]5678ar[1][3]
 ar[2][1]9101112ar[2][3]


This type of order is called row major order and is a good idea to do calculations in this order.  Make sure that your loops follow the "major order" of your two dimensional arrays in order to assure accuracy.  If you always process in "row major" order you will preserve cache and speed up calculations.

Array Declaration/Initialization:

To declare an array:  ar [3][3];      // second array has to be given value always
                 ar [ ][3];      // also valid because second is given value
                 ar [ ][ ];      // **illegal declaration**


Must give second a value because when it sets area up in memory it doesn't need to know how many rows but it does need to know how many elements in each rows.  It can always add more rows to the bottom but can't jam any more in a row that the allowed space.

To initialize:  ar2 [5] = {0,1,2,3,4};
          ar3 [2][2] = {{3,4},{5,2}};
          ar4 [ ] = {2,7,8,0,8};


Passing arrays to functions:

void f1(int ar [5]);       // declaration prototype
void f2(int ar[ ], int n); // n is number of elements in array
void f3(int [ ]);          // needs a "marker value" (\0) to show end
void f4(int ar*);          // really works the same as f3

now for 2D arrays…essentially the same thing is applicable:

void f5(int ar[5][5]);
void f6(int ar[ ][ ], int a, int b);

sleep( );

the sleep function is a way to get your program to pause for a given period in seconds.  It implement it's use you need to include the following:

#include <unistd.h>
sleep(3);  // sets 3 second pause

rand( );

the rand function will return a pseudo-random computer generated number at it's calling.
#include <cstdlib>
num = rand( );     // num is integer random number is assigned to.

srand( );
The srand function will seed the random generator so that it will not always start with the same "random" numbers:
#include <cstdlib>
srand(n);   // n is an unsigned int seed value

This is better but the seed has to be set by the user.  If you really want to have a randomly picked number you need to seed the srand( ) with a different number everytime.  In order to do this you can use a function called time( ) which gets time in seconds since 00:00:00 UTC Jan. 1, 1970

time( );
#include <sys/types.h>
#include <time.h>

time_t time(time_t *tloc);    // need clarification here


Pointer arithmetic:
following are several examples of pointer arithmetic and it's many uses:

int j;
char *p = "Hello"

cout << j would print out a hex number such as 0x8364BD indicating the location in memory of the beginning of the array.

if you take p+2 the result is another pointer which points to a place 2 characters further in memory.

if p points to "H"
p+2 points to "l"

In an array of integers:   int q[]={-7,3,21,6};

-7321 6"\0"

q points to -7 and q+2 points to 21

What's important is that the size of an integer is different from the size of a character:

p+2 is 2 bytes further in memory because it's a char
q+2 is 8 bytes further in memory because it's an int

the statement…      char *p = "Hello";
is the same as…     char p[] = {`H', `e', `l', `l', `o'};

you can't add pointers to pointers because a pointer is an absolute location in memory. BUT you can do subtracting…  Say that you have the array:

W
o
w
!
\0


Assume that the array is q[ ].  The variable char *q is the address of the beginning of the array (or `W')  If you want to find how long the array is, you use another pointer, shall we say char *r, and step through the array until you find `\0' incrementing r as you go. Once you find the null terminator you take the difference between the two and that is the length of the string ( r-q=5 ).

This will work as long as the pointers are of the same data type. You will get the # of elements between the two pointers.

Let's create some arrays for pointer arithmetic practice:

int a1 [ ] = {1, 3, 5, 7, 9};
int a2 [ ] = {11, 13, 15, 17, 19};
int a3 [ ] = {21, 23, 25, 27, 29};
int a4 [ ] = {31, 33, 35, 37, 39};  //   five separate arrays
int a5 [ ] = {41, 43, 45, 47, 49};  //   of contiguous sets of numbers
int *p [ ] = {a1, a2, a3, a4, a5};  //   array of pointer to ints Each
                                    //   points to start of array

Note: if you have arrays of different size this works well because the length of each row is different.

in Memory it looks like this:

                                   a1     1     3      5      7      9
             p                         
                                   a2    11    13   15   17    19

                                   a3    21    23   25   27    29

                                   a4    31    33    35   37    39

                                   a5    41    43    45   47    49


p[2]           - gives address of pointer to int array a3
p[2][3]       - points to third element of second array   (27)
(p+2)[3]    - gives address of start of a3 and third element   (27)
(*p)[3]       - points to a1 position 3   (7)
*p[3]          - points to a4 then de-references to value   (31)
*(p[3])       - same as above   (31)
**(p+2)      - address of element  #2 in p array (a3) then the de-ref. gives first element in a3   (21)
*(p[1]+3)   - goes to p[1] which is address to a2 then goes 3 spaces in and de-references   (17)
*(p+3)[1]   - points to a4 and sees that as p so [1] points to a5 and de-references spot to   (41)

Note:  array subscripting comes before de-referencing.


L-value:

an l-value is anything you can put on the left side of a statement.  It is someplace where you can actually store a value.  In the case of   b[3]  b[3] has a value but b is not an L-value because you can't assign a value to b.

Swap Routines and improvements:

a function for a general swap routine would appear as follows:

void swap(int *a, int *b)
{
     int temp;

     temp = *a;
     *a = *b;
     *b = temp;
}

to call this you would code…     swap(&ar[j], &ar[k]);

This happens so often that in C++ they made a better way:

Reference:

now you can swap like this:

void swap(int &a, int &b)
{
     int temp;

     temp = a;
     a = b;
     b = temp;
}

to call this you would code…     swap(ar[j], ar[k]);

Notice that when in the function you can use the values referenced into the function as you would outside the function and the actual values would be given.

pointers are L-values.  References get you right to what you are referring to so when this is done the values are changed.  Remember that you always have to use L-values when you swap.  You can't call something like    swap(3,j);     // this is a no-no

examples of initializations…

int j;          
int *p;     

p = &j;

int &r =j          // a reference must be initialized to something

later on if you change j to j=7 and then code  cout << r;  the output will be 7 because r points to j.

Const:

Whatever type you apply it to cannot be changed.  It is much the same as the #define statement
Example:

#define MY_SYMBOL 3
is the same as…
const int MY_SYMBOL = 3;

What really happens is that when using the #define the compiler will go through the source code before compiling and change each instance of the variable to the value it holds.  The problem with this is that in some other function, say a library function, if someone used the same symbolic constant using #define it could be changed having disastrous consequences.

Using const will not allow the variable value to be changed.  It is much better to use in most cases.  const can be used with floats, doubles, pointers…

The proper way to read const variables is from right to left.

Example:
const char p[ ] = "Wow!";
char const p[ ] = "Wow!";
char *const p = "Wow!"

These are essentially pretty much the same and are read:
1st   p is an array of characters which are constant.
2nd  p is an array of const char
3rd  p is a constant pointer to characters

Difference:
You can change pointers but you can't change constants.  For example:

const char *p = "hello";
const char *q = "wow";

you can't  code p[0] = "j";
but you can say p=q.

But if you code:
char *const p = "hello";
char *const q = "wow";

now you might be able to code p[0] = "j";
but not p=q.  This would give error because p is declared as a constant pointer

References:
the advantage of using references is that when you pass by copy and you are passing something very large it takes a lot longer.  This isn't largely evident in things on a smaller scale but imagine if you were passing an array of structs holding phone numbers for the city of Chicago.  When you pass by reference you only pass in a location instead of a copy of the entire array.

examples of prototypes and calling statements:

Header                                             calling statement
void f1(Widget w)         uses   f1(w1);
void f2(Widget *w)        uses   f2(&w1);
void f3(Widget &w)        uses   f3(w1);
void f4(const Widget &w)  uses f4(w1)  //guarantees Widget isn't changed.

if you de-reference a pointer to a class or struct you have to use either..
(*p).field    or      w->field

get in the habit of passing by reference and if you don't want it changed, make it const.

File Input/Output:
with streams, files are surprisingly easy.  To use a file:  #include<fstream>   deals with streams involving files.  There are three choices available.

ifstream  //  input file stream
ofstream  // output file stream
fstream   // works for input or output

once opened the file can be used like any input or output stream.

fstream inp,out;

input >> j;          // this works reading int, float, double…
out << "Hello";

But how can I open the file??

inp.open("name", flags)

flags:
ios::in      //opens for input
ios::out     //opens for output
ios::binary  // opens for binary input/output
ios::app     // appends to the end of the file
ios::trunc   // truncates and cleans everything out to zero length

So to open a file named mydata for binary input you would do the following:

ifstream inp; // given name for datafile
inp.open("mydata", ios::in|ios::binary); // literal name/path of file

You can also open a file for input and output at the same time.

After you are done with the file ALWAYS close it.  Use the command, ip.close();

There are two ways to read in C - fscan and fread.  Same problem in C++ so there are other functions that work in conjunction with in and out…  read is like fread in C (byte by byte)

     inp.read(char *buf, int n); // char *buf , where to read to, n is number of bytes to read.

to write in binary fashon:
     inp.write(char *buf, int n);  // same specs apply

in C you use fopen to open a file and check for "NULL"  In C++ you can use   if(cin)..
Example:

     inp.open("file.dat", ios::in);
     if (!inp)   // if it opened correctly the test will be OK
              cout << "error";   // if not test will be 0 or condition false


to test for end of file:
     while(!inp.eof())

Hungarian notation:
When you name your variables try to follow the rule that places the first letter of the variable name as the first letter of the type of variable it is.  This keeps you in a better frame of mind as to what you are working with.


Passing 2D arrays to functions:
arrays and pointers are very similar but arrays set aside memory block whereas pointers don't.