Yan Shi CS/SE2630 Lecture Notes 2. Pointer Yan Shi CS/SE2630 Lecture Notes
C++ Data Types integral enum floating float double long double simple structured integral enum floating float double long double array struct union class char short int long bool address pointer reference
What is reference? simple data type: reference operator & the address of a variable of certain data type int num = 10; int &rNum = num; Do you remember? an array or object must be passed as a reference parameter int nums[10]; Student stu; StudentList stuList; … avg = Average(nums); stuList.Add(stu); Once a reference is created, it cannot be later made to refer another variable/object; it cannot be reseated. int Average( const int myArray[] ); void Add( const Student& stu ); 1. A pointer can be re-assigned any number of times while a reference cannot be re-seated after binding. 2. Pointers can point nowhere (NULL), whereas reference always refer to an object. 3. You can't take the address of a reference like you can with pointers. 4. There's no "reference arithmetics" (but you can take the address of an object pointed by a reference and do pointer arithmetics on it as in &obj + 5).
What is a pointer variable? A pointer variable is a variable whose value is the address of a location in memory. Unlike a reference variable, a pointer can redirect to other locations later. To declare a pointer variable, you must specify the type of value that the pointer will point to. int *p; // p will hold the address of an int char *q; // q will hold the address of a char int a, *b; // * is paired with the identifier. // In this case, we have a int variable a and // a pointer of int type b 4
For a normal variable int num; . Memory Address identifier 0010 ? num
For a normal variable int num; num = 50; . Memory Address identifier 0010 50 num .
Pointer int num; num = 50; int *p; A pointer variable contains the . Memory Address identifier 0010 50 num 0012 ? p A pointer variable contains the memory address of another variable. .
Pointer int num; num = 50; int *p; p = # . Memory Address identifier 0010 50 num 0012 0010 p & is the reference(address-of )operator. .
Pointer int num; num = 50; int *p; p = # cout << *p; . Memory Address identifier 0010 50 num 0012 0010 p . (*) here is the dereference operator. *p is used to access the place p points to. you will see 50 on the screen.
Pointer int num; num = 50; int *p; p = # cout << *p; . Memory Address identifier //direct addressing 0010 100 num 0012 0010 p . //indirect addressing change the value at the address p points to 100
Another Example char ch; ch = ‘A’; char* q; q = &ch; *q = ‘Z’; char* p; p = q; // the right side has value 4000 // now p and q both point to ch 4000 A Z ch 5000 6000 4000 4000 q p
NULL pointer Use NULL to initialize pointers that don’t currently point to anything. used to initialize pointers can be converted to pointers of any type <cstddef> int *p = NULL; It is an error to dereference a pointer whose value is NULL. It is the programmer’s job to check for this.
Dynamic Memory Allocation In the previous example, memory space for num and p are statically allocated at compile time from stack memory (activation records) Dynamic memory allocation at run time from heap memory (free store: dynamic) In java, all user-defined types are allocated from heap In C++, use new operator to get data from heap http://www.learncpp.com/cpp-tutorial/79-the-stack-and-the-heap/ The memory a program uses is typically divided into four different areas: The code area, where the compiled program sits in memory. The globals area, where global variables are stored. The heap, where dynamically allocated variables are allocated from. The stack, where parameters and local variables are allocated from.
Dynamic Memory Allocation int *p = new int; . Memory Address identifier 0010 p .
Dynamic Memory Allocation int *p = new int; . Memory Address identifier 0010 p . unnamed dynamically allocated integer variable (from heap) 0080 ?
Dynamic Memory Allocation int *p = new int; With Initialization: int *p = new int(99); . Memory Address identifier The dynamically allocated variable can only be indirectly addressed through the pointer returned by new. 0010 0080 p . unnamed dynamically allocated integer variable (from heap) 0080 ?
What does new do? It takes a pointer variable, allocates heap memory for it to point, and leaves the address of the assigned memory in the pointer variable. If there is no more memory, the pointer variable is set to NULL.
Dynamic Array Using new, now we can dynamically decide the size of an array. int size; cin >> size; char *text = new char[size];
Memory Leak Memory is allocated but not released causing an application to consume memory reducing the available memory for other applications and eventually causing the system to page virtual memory to the hard drive slowing the application or crashing the application when the computer memory resource limits are reached. Example: int *p1 = new int; int *p2 = new int(99); *p1 = 10; p2 = p1; // The memory cell p2 originally // points at now can no longer be // accessed this is called garbage.
Deallocate Memory: delete delete operator is used to return to the heap a memory location allocated previously by the new operator. A pointer p is pointing to a dynamically allocated space. When to delete p? p is about to point to another space and no other pointer is pointing at the same location as p; right before the program exit. int *p1 = new int; int *p2 = new int(99); *p1 = 10; delete p2; // This prevents memory leak. p2 = p1; … int *a = new int[n]; delete[] a; // deallocate the entire array space.
Enable Memory Leak Detection Visual Studio provides C Run-Time Libraries (CRT) debug heap functions. To enable: include in the exact order. add _CrtDumpMemoryLeaks(); immediately before the program exit. When you run your program under the debugger, _CrtDumpMemoryLeaks displays memory leak information in the Output window. #define _CRTDBG_MAP_ALLOC #include <stdlib.h> #include <crtdbg.h> By including crtdbg.h, you map the malloc and free functions to their debug versions, _malloc_dbg and _free_dbg, which keep track of memory allocation and deallocation. This mapping occurs only in a debug build (in which _DEBUG is defined). Release builds use the ordinary malloc and free functions. The #define statement maps the base versions of the CRT heap functions to the corresponding debug versions. You do not absolutely need this statement, but without it, the memory leak dump will contain less useful information.
Stack vs Heap Memory Summary Memory allocated on the stack stays in scope as long as it is on the stack. It is destroyed when it is popped off the stack. All memory allocated on the stack is known at compile time. Consequently, this memory can be accessed directly through a variable. Because the stack is relatively small, it is generally not a good idea to do anything that eats up lots of stack space. This includes allocating large arrays, structures, and classes, as well as heavy recursion. Heap: Allocated memory stays allocated until it is specifically deallocated (beware memory leaks). Dynamically allocated memory must be accessed through a pointer. Because the heap is a big pool of memory, large arrays, structures, or classes should be allocated here.
Pointers and Arrays C++ arrays are not objects as in Java. They are really just pointers! char name[30]; // name is actually &name[0] char *np; np = &name[0]; // same as np = name; C++ allows pointer arithmetic: … cin >> *np; while( *np != ‘/n’ ) { np++; } name is a constant pointer. name[i] is the same as *(name + i) 1) Precedence of prefix ++ and * is same. Associativity of both is right to left. 2) Precedence of postfix ++ is higher than both * and prefix ++. Associativity of postfix ++ is left to right. // hope that all names are // shorter than 30 characters // moves np ahead sizeof(char) bytes // and points to the next element.
Pointer Arithmetics char b, *p, s[9] = “SOFTWARE” b = *(s+1)+1;? p = s+7; b = *--p;? Precedence of prefix ++ and * is same. Associativity of both is right to left. Precedence of postfix ++ is higher than both * and prefix ++. Associativity of postfix ++ is left to right. *p++ // same as *(p++): increment pointer, and dereference unincremented address *++p // same as *(++p): increment pointer, and dereference incremented address ++*p // same as ++(*p): dereference pointer, and increment the value it points to (*p)++ // dereference pointer, and post-increment the value it points to *p++ = *q++; *p = *q; ++p; ++q;
Pointers and Objects How to declare an object? Student stu(…); OR Student *stu = new Student(…); For the second declaration, we can make a public method call like this: stu->GetGPA(); // stu is a Student object // located at the stack memory // stu is a pointer of Student type // located at the heap memory // This is the same as // (*stu).GetGPA();
Pointers and Objects We can make a dynamic array of objects: Student * stuList = new Student[n]; In this case, Student must have a default constructor! An alternative is to make a dynamic array of Student pointers Student **stuList = new Student*[n]; In this case, no default constructor is needed, but memory management becomes more complicated.
Pointers and const int x, y = 10; const int * p = &y; x = *p; // ok:reading p *p = x; // error: modifying p, which is const-qualified Const pointers can read the value pointed, but cannot modify it. As a safety feature, pointers to const are not implicitly convertible to pointers to non-const. A function that takes a pointer to non-const as parameter can modify the value passed as argument, while a function that takes a pointer to const as parameter cannot. void print_all (const int* start, const int* stop) { const int * current = start; while (current != stop) { cout << *current << '\n'; ++current; // increment pointer }
Dangling Pointer Pointers that do not point to a valid object. Dangling pointers arise when an object is deleted or deallocated, without modifying the value of the pointer, so that the pointer still points to the memory location of the deallocated memory. If later the program dereferences the (now) dangling pointer, unpredictable behavior may result. That is why Java introduced automatic garbage collection!
Dangling Pointer Example int* ptr = new int; *ptr = 8; int* ptr2 = new int; *ptr2 = -5; ptr = ptr2; 8 ptr -5 ptr2 8 cannot be addressed and thus become a garbage
Dangling Pointer Example 8 ptr NULL ptr2 int* ptr = new int; *ptr = 8; int* ptr2 = new int; *ptr2 = -5; ptr = ptr2; delete ptr2;// ptr is left dangling ptr2 = NULL;
Dangling Pointer Example common mistake: returning address of local data Both create dangling pointers! xyz will be deleted after the function call returned pointer will be pointing to empty slot. X* foo() { X xyz; ... operate on xyz ... return &xyz; } char* g() { char str[100]; ... operate on str ... return str; }
Void Pointer void pointers are pointers that point to a value that has no type it can point to objects of any type! void * p; int nValue = 10; char str[] = “Hello”; p = &nValue; p = str; A void pointer must be explicitly cast into another type of pointer to be dereferenced. cout << *static_cast<char*>(p) << endl; In general, it is a good idea to avoid using void pointers unless absolutely necessary, as they effectively allow you to avoid type checking. What’s the difference between a void pointer and a null pointer? A void pointer is a pointer that can point to any type of object, but does not know what type of object it points to. A void pointer must be explicitly cast into another type of pointer to be dereferenced. A null pointer is a pointer that does not point to an address. A void pointer can be a null pointer. static_cast can perform conversions between pointers to related classes, not only upcasts (from pointer-to-derived to pointer-to-base), but also downcasts (from pointer-to-base to pointer-to-derived). dynamic_cast can only be used with pointers and references to classes