CSCE 121 Chapter 17
« 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();