170 likes | 292 Views
Motivation and Overview. Often need to refer to another object Without making a copy of the object itself Two ways to do this Indirectly, via a pointer Gives the address of the object (analogy: street address) Requires the code to do extra work: dereferencing
E N D
Motivation and Overview • Often need to refer to another object • Without making a copy of the object itself • Two ways to do this • Indirectly, via a pointer • Gives the address of the object (analogy: street address) • Requires the code to do extra work: dereferencing • Like going to the given address, to talk to the person • Directly, via a reference • Acts as an alias for the object • Code interacts with reference as if it were object itself
A variable holding an address Of what it “points to” in memory Can be untyped E.g., void * v; // points to anything However, usually they’re typed Checked by compiler Can only be assigned addresses of variables of type to which it can point E.g., int * p; // only points to int Can point to nothing E.g., p = 0; // or p = nullptr; (C++11) Can change where it points As long as pointer itself isn’t const E.g., p = &i; // now points to i Review: What’s a Pointer? int i 7 0x7fffdad0 int *p
Also a variable holding an address Of what it “refers to” in memory But with a nicer interface A more direct alias for the object Hides indirection from programmers Must be typed Checked by compiler Again can only refer to the type with which it was declared E.g., int & r =i; // refers to int i Always refers to (same) something Must initialize to refer to a variable Can’t change what it aliases Review: What’s a Reference? int i 7 0x7fffdad0 int & r
int main (int argc, char *argv[]) { int i = 0; int j = 1; int * p = & i; int * q = & i; *q = 6; // i is now 6, j is still 1 } Distinct variables have different memory locations E.g., i and j A variable and all the pointers to it (when they’re dereferenced) all alias the same location E.g., i, *p, and *q Assigning a new value to i, *p or *q changes value seen through the others But does not change value seen through j Aliasing and Pointers int *p 0xefffdad0 6 int i int *q 0xefffdad0 1 int j
int main (int argc, char **argv) { const int i = 0; int j = 1; int k = 2; // pointer to int int * w = & j; // (array names are like const // pointers to their 0th position) // const pointer to int int * const x = & j; // pointer to const int const int * y = & i; // const pointer to const int const int * const z = & j; } Read declarations right to left Make promises via the const keyword in pointer declaration: not to change where the pointer points not to change value at the location to which it points Can (only) change Where w points and the value at the location to which it points The value at the location to which x points Where y points A pointer to non-const cannot point to a const variable neither w nor x can point to i any of them can point to j Review: Const Pointers
int main (int argc, char **argv) { int arr [3] = {0, 1, 2}; int * p = & arr[0]; int * q = arr; // p, q, arr point to same place } An array holds a contiguous sequence of memory locations Can refer to locations using either array index or pointer notation E.g., *arr vs. arr[0] E.g., *(arr+1) vs. arr[1] Array variable essentially behaves like a const pointer Likeint * const arr; Can’t change where it points Can change locations unless declared array-of-const E.g., const int arr[3]; Can initialize other pointers to the start of the array Using array name, or using address of 0th element Pointers and Arrays int arr [3] 0 1 2 0xefffdad0 0xefffdad0 int *p int *q
int main (int argc, char **argv) { int arr [3] = {0, 1, 2}; int * p = & arr[0]; int * q = arr; // p, q now point to same place ++q; // now q points to arr[1] } Adding or subtracting int n moves a pointer by n of the type to which it points I.e., by n array positions E.g., value in q is increased by sizeof(int) by ++q Can move either direction E.g., --q, ++p Can jump to a location E.g., p+=2, q-=1 Remember that C++ (only) guarantees that sizeof(char)==1 But compiler figures out other sizes for you Pointer Arithmetic With Arrays int arr [3] 0 1 2 0xefffdad0 0xefffdad0 int *p int *q
int main (int argc, char *argv[]) { // could declare char **argv for (int i = 0; i < argc; ++i) cout << argv[i] << endl; } Can have pointers to pointers Can also have an array of pointers (like a const pointer to a pointer type) E.g., argv parameter in main Array of pointers to character strings Could also declare as a pointer to the first pointer Notice array dimension is not specified Instead a special argument (argc) holds array size By convention, character strings are zero terminated Special char is ‘\0’ not ‘0’ Arrays of (and Pointers to) Pointers t e s t \0 h e l l o \0 0xefffa0d0 0xefffaad0 char * argv[] 2 int argc
int main (int argc, char **argv) { int arr [3] = {0, 1, 2}; int * p = & arr[0]; int * q = p + 1; return 0; } You can subtract (but not add, multiply, etc.) pointers Gives an integer with the distance between them You can add/subtract an integer to/from a pointer E.g., p+(q-p)/2 is allowed but (p+q)/2 gives an error Note relationship between array and pointer arithmetic Given pointer p and integer n, the expressions p[n] and *(p+n) are both allowed and mean the same thing Rules for Pointer Arithmetic int arr [3] 0 1 2 0xefffdad0 0xefffdad0 int *p int *q
int main (int argc, char **argv) { int arr [3] = {0, 1, 2}; int * p = & arr[0]; int * q = arr; // p, q now point to same place int n; q+=n; // now where does q point? } Dereferencing a 0 pointer will crash your program Accessing memory location outside your program can Crash your program Let you read arbitrary values Let you modify that location Last two: hardest to debug Watch out for Uninitialized pointers Failing to check pointer for 0 Adding or subtracting an uninitialized variable to a pointer Errors in loop initialization, termination, or increment Watch out for Pointer Arithmetic Errors int arr [3] 0 1 2 0xefffdad0 0xefffdad0 int *p int *q
Foo * baz (){ // note the array form of new int * const a = new int [3]; a[0] = 0; a[1] = 1; a[2] = 2; Foo *f = new Foo; f->reset(a); return f; } void Foo::reset(int * array) { // ctor must initialize to 0 delete [] this->array_ptr; this->array_ptr = array; } void Foo::~Foo() { // note the array form of delete delete [] this->array_ptr; } We’ve talked mainly about stack variables so far Arrays, like individual objects, can be allocated (new) and de-allocated (delete) dynamically more details in later lectures Arrays have a particular syntax with [ ] in the new and delete calls Ensures constructors and destructors of elements are called automatically Don’t leak, destroy safely Requires careful attention to details of memory management More about this in next lectures Arrays, Pointers, and Dynamic Allocation
int main (int argc, char **argv) { int i = 0; int j = 1; int & r = i; int & s = i; r = 8; // i is now 8, j is still 1 } An object and all the references to it alias the same location E.g., i, r, and s Assigning a new value to i, r or s changes value seen through the others But does not change value seen through j Aliasing and References int & r 0xefffdad0 8 int i 0xefffdad0 1 int j int & s
int main (int argc, char **argv) { const int i = 0; int j = 1; int & r = j; // r can’t refer to i const int & s = i; // ok for s,t const int & t = j; // to alias i,j } Remember: references must refer to something Can’t be nil Also, once initialized, they cannot be changed E.g., can’t redirect t to i Const with a reference A promise not to change what’s aliased E.g., can’t use t to change j Can’t use reference to non-const with a const variable, but reverse is ok References to Const
int main (int argc, char **argv) { int h = -1; int i = 0; int j = 1; int k = 2; return func (h, i, j, & k); } int func (int a, const int & b, int & c, int * d) { ++a; c = b; *d = c ++d; return 0; } By value Makes a copy i.e., of h into local variable a ++a does not change h By reference Alias for passed variable c = b changes j can’t change b (or i): const Can pass address by value And then use address value to change what it points to *d = c changes k ++d changes local pointer Review: Parameter Passing
int main (int argc, char **argv) { int j = 1; int & r = j; // r aliases j int * p = & r; // p really // points to j int * & t = p; // t aliases p } Can’t have a pointer to a reference But can point to what the reference aliases Address-of operator on a reference to a variable Gives address of variable … not of reference itself Reference to a pointer An alias for the pointer … not for what it points to Useful to pass a pointer to code that may change it References to Pointers int & r 0xefffdad0 1 int j 0xefffdad0 int * p int * & t 0xefffdad0
Why to Use STL Vectors Instead of C++ Arrays • #include <iostream> • #include <vector> • using namespace std; • int main (int, char *[]) { • vector<int> v; • // This would be asking for trouble.... • // v[0] = 1; v[1] = 2; v[2] = 4; • // ... but this works fine... • v.push_back (1); • v.push_back (2); • v.push_back (4); • // ... and now this is ok ... • for (size_t s = 0; s < v.size(); ++s) { • cout << "v[" << s << "] is " • << v[s] << endl; • } • return 0; • } • Vectors do a lot of (often tricky) memory management for you • Use new[] and delete[] internally • Resize memory as needed • Don’t leak memory • Easier to pass to functions • E.g., can tell you their size • Don’t have to pass a separate size variable • Don’t need a pointer by reference in order to resize • Still have to pay some attention • E.g., push_back can allocate more memory but operator [] can’t • E.g., vectors copy and take ownership of objects they contain