Static Arrays

Dynamic arrays vs static arrays

There are two types of arrays in C++:
Creating an Array of 6 ints
Static Array Dynamic Array

int A[6];

int* A = new int[6];
Using the array after its been created is pretty much the same for either.

Q: For a static array A, do you have to delete [] A, when you don't need it anymore?

No. (Only when A is created by new int [6]). 

You can sometimes write simpler code using static arrays

Anything you can do with static arrays you can do with dynamic arrays, and then some. However, in some instances, using static arrays is simpler. For example,

Difference between dynamic and static arrays

Note the main difference between dynamic and static arrays:

If A is a static array, the pointer A cannot be changed.

To understand the difference between this version of Quad and the previous version, consider this picture:

Note:

Compare the static array version of main with the dynamic array version of main.

The above picture really tells you all you need to know to understand the difference between using static and dynamic arrays ... when you really can use static arrays.

Q: Can you change the contents of a static array?

A: Of course, yes

Quick check

Let's look at one example to see what consequences arise from this picture.
Q: Suppose that I have a Quad object S that contains the label 'Q' and the vertices (0,0) (1,0) (1,1) (0,1). I assign S to a new Quad object R and change the label in R to 'P'. I then print out S and then R. What will I get?

A: It depends whether I'm using the static version of Quad or the dynamic version. (Drag your mouse for answers)

Static
Version
Q (0,0) (1,0) (1,1) (0,1)
Q (0,0) (1,0) (1,1) (0,1)
P (1,0) (2,0) (2,1) (1,1)
Dynamic
Version
Q (0,0) (1,0) (1,1) (0,1)
Q (1,0) (2,0) (2,1) (1,1)
P (1,0) (2,0) (2,1) (1,1)

// step 1
Quad S;
...       // S has 'Q' and (0,0), (1,0), (1,1), and (0,1) 
print(S); // It will print Q (0,0) (1,0) (1,1) (0,1)

// step 2
Quad R;
R = S;    // Copying will have different meanings!
R.label = 'P';    

// step 3
for(int i = 0; i < 4; i++)
  R.vert[i].x++;

print(S);
print(R);
Why the difference? Look at the picture!

Dynamic Version Static Version
This is not a reason to use static over dynamic, but it is a good example of how and why they behave differently.

Swapping the static arrays

Here's another example:

Q: How does swap(A,B) behave differently for two Quads, A and B, with the dynamic versus static array versions of Quad?

A: Once again, the above picture should tell you that while the result is the same, a lot more work gets done in the static case, where the entire contents of the arrays are swapped, rather than simply the pointers.

Guarding header files

Rule for struct definition

When source code files are compiled in C++, there is a rule about how often struct definitions can appear.

Problem

Now, there's one problem that can come up, which is illustrated by the following example:

point.h point.cpp main1.cpp

struct point
{
  double x, y;
};
point readpoint();

#include "point.h"
#include <iostream>
using namespace std;

point readpoint()
{
  point p;
  char c;
  cin >> c >> p.x
      >> c >> p.y >> c;  
  return p;
}

#include "point.h"     // NOTE HERE
#include "triangle.h"  // NOTE HERE

int main()
{
  triangle T = readtriangle();
  point P = T.vertex[0];
  return 0;
}
triangle.h triangle.cpp

#include "point.h"
struct triangle
{
  point vertex[3];
};
triangle readtriangle();

#include "triangle.h"

triangle readtriangle()
{
  triangle t;
  for(int i = 0; i < 3; i++)
    t.vertex[i] = readpoint();
  return t;
}
Now look what happens when we try to compile:
$ g++ -c point.cpp
$ g++ -c triangle.cpp
$ g++ -c main1.cpp
In file included from triangle.h:1:0,
                 from main1.cpp:2:
point.h:1:8: error: redefinition of 'struct point'
point.h:1:8: error: previous definition of 'struct point'
The problem is that this causes the struct point to be defined twice in main.cpp. Why? Admittedly, if the programmer was on top of things he could've avoided this, but it's not always avoidable, and it's expecting a lot of the guy who uses structs point and triangle to go back and worry about this stuff.

Fix: guard your header files

The way around this problem is to guard your .h files with #defines. The #include that we've been using and the #define that we're about to use are examples of "preprocessor directives". We'll use #define to determine whether or not this is the first time the compiler has seen our .h while compiling a given .cpp file. For example
code If SILLYSTRING has not been #defined If SILLYSTRING has been #defined
  
  cout << "This is ";
#ifndef SILLYSTYRING
  cout << "not ";
#endif
  cout << "much fun!" << endl;

  cout << "This is ";
  cout << "not ";
  cout << "much fun!" << endl;

  cout << "This is ";
  cout << "much fun!" << endl;
If SILLYSTRING has not been #defined previously in our program, the word not will be printed out --- the #ifndef SILLYSTRING asks if it's true that SILLYSTRING hasn't been defined. If it is, then all the text until the #endif is seen by the compiler. Otherwise, it is as if the code in between was never there.

How does this help us with our multiple-definition problem?

point.h triangle.h main.cpp

#ifndef POINTHEADER  // NOTE HERE 
#define POINTHEADER  // NOTE HERE

struct point
{
  double x, y;
};
point readpoint();

#endif              // NOTE HERE

#include "point.h"
struct triangle
{
  point vertex[3];
};
triangle readtriangle();

#include "point.h"
#include "triangle.h"

int main()
{
  triangle T = readtriangle(); 
  point P = T.vertex[0];

  return 0;
}
  1. Now, in compiling main.cpp we first #include the file point.h. Since this is the first time the compiler has seen point.h it'll find that POINTHEADER has not been defined. Therefore all the code until the #endif, which is essentially the whole file, will be seen and evaluated by the compiler. This will include the line that #defines POINTHEADER.
  2. Now, the next time we come across point.h, which is when main.cpp does the #include "triangle.h", the compiler will hit that #ifndef line and will find that POINTHEADER has been defined, and therefore everything up to the #endif, which is essentially the whole file, will be ignored by the compiler. This way we never get any multiple definitions.
All .h files should be "protected" by #ifndefs this way!

Practice Problems

  1. Write a program that reads a date in mm/dd/yyyy format and prints it out in "dd monthname yyyy" format.

    Tip: It might be helpful to know that a static array can be initialized with a list of values in { }'s. For example, an array of the first 10 prime numbers can be constructed like this:

    int prime[10] = {2,3,5,7,11,13,17,19,23,29};
    Note: this problem is purely about static arrays, it doesn't concern structs at all. Here's a solution.