CSCE 121 Chapter 17

From Notes
Jump to navigation Jump to search
End Exam 2 Content

« previous | Monday, October 25, 2010 | next »

Pointers

Vector

very useful container

Holds an arbitrary number of elements that can vary over time (push_back())

age

age[0] age[1] age[2] age[3]
0.33 22.0 27.2 54.2

How is it implemented?

Hardware provides memory and addresses:

  • low-level
  • untyped (no defined type)
  • no checking
  • as fast as hardware architects make it

We need higher level access and checking

Simple implementation

class vector {
  int sz;        // number of elements ("size")
  double* elem;  // elem is a pointer to type double

  vector(int s) : sz(s), elem(new double[s]) {}  // store size s in sz and allocate that many doubles on free storage; new keyword returns pointer to location
}

Pointers: Free store ("heap")

Request memory to be allocated on free store using new operator:

  • new returns pointer to allocated memory on the free store, but does not initialize unless the constructor is called (int* pi = new int(5))
  • pointer is address of first byte of allocated memory
  • all data members are initialized in the free store; nothing is put on the stack.

p: [ ] → [ | | | | ]

int* p1 = new int;     // allocate space for an int
int* p2 = new int(5);  // allocate space for an int and set it to 5

int x = *p2;           // get contents of what p2 points to (integer 5)
int y = *p1;           // get contents of what p1 points to (undefined)

To allocate objects that have to oulive the function that creates them:

double* make(int i) {
  // create an array of doubles that has i 
  return new double[i];
}


Wednesday, October 27, 2010


int* p3 = new int[5];  // allocate 5 ints; array numbered 0, 1, 2, 3, 4
p3[0] = 7;  // set first int to 7
p3[1] = 9;  // set second int to 9

int x1 = p3[1];   // x1 = 9
int x2 = *p3;     // x2 = 7;  p3[0] == *p3

Bracket operators act as dereferencers, only they define how many steps past the pointer to go.

Pointer values are memory addresses

  • think of them like integer values
  • first byte of memory is 0, next is 1, etc.
char* p1 = new char('c');   // allocate char and initialize it to 'c'
int* p2 = new int(7);       // allocate int and initialize it to 7
cout << "p1==" << p1 << " *p1==" << *p1 << endl    // p1==??? *p1==c
     << "p2==" << p2 << " *p2==" << *p2 << endl;   // p2==??? *p2==7


Warnings

Pointers do not know their length:

// problem...
double* p1 = new double[2];
*p1 = 7.3;     // ok
p1[0] = 8.2;   // ok
p1[17] = 9.4;  // ouch! undetected error
p1[-4] = 2.4;  // ouch! undetected error

double* p1 = new double;
double* p2 = new double[100];

// but...

p1[17] = 9.4;  // error (obviously); no index 17
p1 = p2;       // assign the VALUE of p2 to p1; p2 is a pointer to 100 objects;
p1[17] = 9.4   // now ok: p1 now points to array of 100 doubles

Pointers do know what type of object that they're pointing to:

int* pi1 = new int(7);
int* pi2 = pi1;    // okay; both point to same type of object
double* pd = pi1;  // error: can't assign int* to double*
char* pc = pi1     // error: can't assign int* to char*

*pc = 8;     // okay; we can assign an int to char
*pc = *p1;   // okay for same reason.

With pointers, we are "touching" the hardware with little/no help from C++; can result in serious bugs


More advanced implementation of vector

class vector {
  int sz;
  double* elem;

  public:
    vector(int s) :sz(s), elem(new double[s]) {}
    double get(int n) { return elem[n]; }
    void set(int n, double v) { elem[n] = v; }
    int size() const { return sz; }
};

vector v(10);
for (int i=0; i<v.size(); ++i) {
  v.set(i,i);
  cout << v.get(i) << ' ';  // 0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0
}


Friday, October 29, 2010


Memory Leaks

Occur when free memory is allocated, but not deallocated (released; returned to free store).

delete[]

Use delete keyword to delete single values, and delete[] to delete arrays.

Has to be explicitly called in code (verbose, ugly, and easy to forget) or implicitly called in a destructor ( :) ).

Destructor

Provides code to deallocate memory. Automatically called whenever a variable of that type goes out of scope.

class vector {
  int sz;
  double* elem;
  public:
    vector(int s) : sz(s), elem(new double[s]) {}
    ~vector() {
      delete[] elem;
    }
  // ...
}

Examples of resources that need to be acquired in constructor and released in destructor:

  • memory
  • files
  • locks
  • threads
  • sockets

Example

 void f(int x) {
  int* p = new int[x];  // allocate x ints
  vector v(x);          // define a vector (which allocates another x doubles);
  // ... use p and v ...
  delete[] p;           // deallocate array pointed to by p
}  // v is automatically deallocated with destructor


Null Pointer

Tells code that the pointer does not point to anything

char* p = 0;


void*

Points to a location, but we don't know the type. Note that there is no void type. i.e. we can't call void n;

This is not to be confused with non-returning function declaration (void set_val(int i) { /* ... */ })

Any pointer object can be assigned to a void*:

int* pi = new int;
double* pd = new double[10];
void* pv1 = pi;   // pv1 points to integer
void* pv2 = pd;   // pv2 points to array of doubles
pv1 = pd;         // pv1 now points to array of doubles.  This would be impossible if it weren't for void*

In order to read, we must cast the type to convert the pointer to an object type.

void f(void* pv) {
  void* pv2 = pv;    // okay: copying void* to void*
  double* pd = pv;   // error: cannot convert void* to double*
  *pv = 7;           // error: cannot dereference a void* (for getting or setting)
  pv[2] = 9;         // error: cannot subscript a void* (subscripts are same as dereferencing)
  pv++;              // error: cannot increment a void*

  int* pi = static_cast<int*>(pv);  // okay: explicit conversion from void* to int*
}

static_cast is used to explicitly convert a void* pointer to an object type. This is dangerous and ugly; use only when absolutely necessary.


Pointers and References

Think of reference&s as automatically dereferenced pointer*

int x = 7;
int y = 8;
int* p = &x;  // assign the pointer p to the address of regular variable x
*p = 9;       // set x (the object pointed to by p) to 9
int& r = x;
r = 10;       // set x to 10
int main() {
  double x = 5.0;
  double* px = &x;
  double& rx = x;

  double* a = new double[5];
  for (int i=0; i<5; ++i) a[i]=(double)i;
  *a = 100;    // a[0] = 100;
  a[2] = 200;
}

Accessing Free store objects

vector* p = new vector(4);
int x = p->size();    // shorthand to execute member function 'size' on object 'p' in free-store
// OR
int x = (*p).size();