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 |
2 | 3 | 4 | ar[0][3] |
| | ar[1][1] | 5 | 6 | 7 | 8 | ar[1][3] |
| | ar[2][1] | 9 | 10 | 11 | 12 | ar[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};
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:
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.
|