Dangling pointers.

Slides:



Advertisements
Similar presentations
Throwing and catching exceptions
Advertisements

Recursive binary search
For loops.
Templates.
Introduction to classes
Static variables.
Default values of parameters
Pointers.
Dynamically allocating arrays
Anatomy of a program.
Binary search.
Do-while loops.
Command-line arguments
Pointer arithmetic.
Console input.
Dangling pointers.
This.
Dynamically allocating arrays within structures
Dynamic memory allocation
Break statements.
Linked Lists.
Wild pointers.
Logical operators.
The comma as a separator and as an operator
Bucket sort.
The call stack and recursion and parameters revisited
The ternary conditional operator
Dynamically allocating structures
Memory leaks.
Sorting algorithms.
Command-line arguments
Passing pointers as parameters to and from functions
Templated Linked Lists
Dynamically allocating arrays
Insertion sort.
Problems with pointers
A list-size member variable
Protecting pointers.
Dynamically allocating arrays
Code-development strategies
Throwing exceptions.
Anatomy of a program.
Pointers as arguments and return values
Reference variables, pass-by-reference and return-by-reference
Addresses and pointers
Default values of parameters
Pointer arithmetic.
Class variables and class functions
Operator overloading.
The std::string class.
Dynamic allocation of arrays
Templates.
This.
Dynamic memory allocation
Insertion sort.
Sorting algorithms.
Issues with classes.
Dynamic allocation of classes
Encapsulation.
Destructors.
Counting sort.
Searching and sorting arrays
Protecting pointers.
Data structures: class
An array class: constructor and destructor
Constructors.
This.
Recursive binary search
Recursive functions.
Presentation transcript:

Dangling pointers

Outline In this lesson, we will: Review what happens when we call delete See that These pointers still hold their values You can still access and manipulate this deallocated memory This can lead to very difficult-to-find bugs The solution is straight-forward, but requires rigorous control on values

Dangling pointers A pointer has a value If the value is 'nullptr', this is a known invalid address If initialized or assigned a value returned by new, the pointer stores a valid address Once memory is deallocated, it is no longer valid memory A dangling pointer is a pointer that stores an address that is no longer allocated

Dangling pointers Remember that all delete does is pass the value of the operand to the operating system #include <iostream> int main(); int main() { int *p_value{new int{42}}; std::cout << "Before delete: " << p_value << std::endl; delete p_value; std::cout << "After delete: " << p_value << std::endl; return 0; } Output: Before delete: 0x1588010 After delete: 0x1588010

Dangling pointers Because a dangling pointer still stores an address, it is similar to a wild pointer, only the behavior is much worse A wild pointer is almost certainly invalid A dangling pointer stores an address that has previously been allocated Operating systems may flag deallocated memory as available, but may not waste the time to flag it as no longer available This is reasonable: If the same program asks for more memory, if possible it will use this memory first If another program asks for memory and no more memory is availabe, it may then

Dangling pointers Two separate requests for memory will result in two different addresses #include <iostream> int main(); int main() { int *p_value_1{new int{42}}; int *p_value_2{new int{91}}; std::cout << "First new: " << p_value_1 << std::endl; std::cout << "Second new: " << p_value_2 << std::endl; delete p_value_1; delete p_value_2; return 0; } Output: First new: 0x1ea5010 Second new: 0x1ea5030

Dangling pointers If memory has been deallocated, the operating system is welcome to reuse it for future requests #include <iostream> int main(); int main() { int *p_value_1{new int{42}}; std::cout << "First new: " << p_value_1 << std::endl; delete p_value_1; int *p_value_2{new int{91}}; std::cout << "Second new: " << p_value_2 << std::endl; delete p_value_2; return 0; } Output: First new: 0x1ea5010 Second new: 0x1ea5010

Dangling pointers The problem is that this memory can therefore still be accessed: #include <iostream> int main(); int main() { int *p_value{new int{42}}; std::cout << "Before delete: " << *p_value << std::endl; delete p_value; *p_value = 91; std::cout << "After delete: " << *p_value << std::endl; return 0; } Output: Before delete: 42 After delete: 91

Dangling pointers If it can be accessed, it can be assigned: Output: #include <iostream> int main(); int main() { int *p_value_1{new int{42}}; delete p_value_1; int *p_value_2{new int{91}}; std::cout << "Before: " << *p_value_2 << std::endl; *p_value_1 = 42; std::cout << "After: " << *p_value_2 << std::endl; return 0; } Output: Before: 91 After: 42

Dangling pointers It gets worse if the types are different: Output: #include <iostream> int main(); int main() { double *p_value_1{new double{3.141592654}}; delete p_value_1; int *p_value_2{new int{42}}; std::cout << "Before 2: " << *p_value_2 << std::endl; *p_value_1 = 2.718281828; std::cout << "After 2: " << *p_value_2 << std::endl; std::cout << "After 1: " << *p_value_1 << std::endl; return 0; } Output: Before 2: 42 After 2: -1962634853 After 1: 2.71828

Dangling pointers However, once in a blue moon, the operating system may actually take that deallocated memory away and give it to another program #include <iostream> int main(); int main() { int *p_value{new int{42}}; delete p_value; for ( int k{0}; k < 1000000000; ++k ) { // Do nothing... } *p_value = 91; std::cout << *p_value << std::endl; return 0; Output after 57368 executions: Segmentation fault (core dumped)

Dangling pointers This can be solved by always assigning deallocated pointers the value of nullptr: #include <iostream> int main(); int main() { int *p_value{new int{42}}; // Use the value... delete p_value; p_value = nullptr; return 0; }

Dangling pointers You should always assign deallocated pointers the value 'nullptr' even if the function is exiting—a wild pointer may pick up that value vector3d_t center_of_gravity() { vector3d_t return_value; std::size_t capacity = 16; vector3d_t *p_centers_of_mass{new vector3d_t[capacity]{}}; // Use the array to perform the necessary calculation... // - we may have to resize the array delete[] p_centres_of_mass; p_centres_of_mass = nullptr; return return_value; }

Dangling pointers This becomes more difficult if multiple pointers are assigned the same address: #include <iostream> int main(); int main() { int *p_value_1{new int{42}}; std::cout << "Address 1: " << p_value_1 << std::endl; int *p_value_2{p_value_1}; delete p_value_1; p_value_1 = nullptr; std::cout << "Address 2: " << p_value_2 << std::endl; return 0; } Output: Address 1: 0x1dc9010 Address 2: 0x1dc9010

Dangling pointers Avoiding this second problem is more difficult… This requires strict variable management The only real solution is to ensure that only one pointer variable or parameter ever stores a particular address in any one function Suppose you select File→Close from the PowerPoint menu for a particular presentation This calls a function that ultimately deallocates the memory for the presentation It also disables almost all menu items and ribbon icons to prevent you from accessing the deallocated presentation

Warning! The single most greatest difficult for students studying algorithms and data structures is this belief that delete does something magical The statement delete p_data; does nothing more than send the address to the operating system The operating system simply flags the memory at that location as being available for future allocations

Summary Following this lesson, you now Understand that delete does not change the value of a pointer Know that calling delete on a pointer: Allows the operating system to flag the memory as available The operating system, however, usually still leaves the memory allocated to the task…usually When a pointer is deallocated, it must be assigned 'nullptr' Otherwise, delete does nothing to change the value of the pointer

References [1] No references?

Colophon These slides were prepared using the Georgia typeface. Mathematical equations use Times New Roman, and source code is presented using Consolas. The photographs of lilacs in bloom appearing on the title slide and accenting the top of each other slide were taken at the Royal Botanical Gardens on May 27, 2018 by Douglas Wilhelm Harder. Please see https://www.rbg.ca/ for more information.

Disclaimer These slides are provided for the ece 150 Fundamentals of Programming course taught at the University of Waterloo. The material in it reflects the authors’ best judgment in light of the information available to them at the time of preparation. Any reliance on these course slides by any party for any other purpose are the responsibility of such parties. The authors accept no responsibility for damages, if any, suffered by any party as a result of decisions made or actions based on these course slides for any other purpose than that for which it was intended.