Dynamically allocating arrays

Slides:



Advertisements
Similar presentations
Throwing and catching exceptions
Advertisements

Recursive binary search
For loops.
Templates.
Introduction to classes
Static variables.
Pointers.
Dynamically allocating arrays
Anatomy of a program.
Switch statement.
Binary search.
Namespaces.
Command-line arguments
Throwing exceptions.
Pointer arithmetic.
Console input.
Dangling pointers.
This.
Sorted arrays.
Floating-point primitive data types
Dynamically allocating arrays within structures
Dynamic memory allocation
Break statements.
Introduction to classes
Linked Lists.
Wild pointers.
The comma as a separator and as an operator
Selection sort.
Bucket sort.
The ternary conditional operator
Dynamically allocating structures
Memory leaks.
Pushing at the back.
Bit-wise and bit-shift operators
Sorting algorithms.
Command-line arguments
Passing pointers as parameters to and from functions
Polymorphism.
Dynamically allocating arrays
Problems with pointers
A list-size member variable
Protecting pointers.
Code-development strategies
Throwing exceptions.
Anatomy of a program.
Selection sort.
Pointers as arguments and return values
Reference variables, pass-by-reference and return-by-reference
Addresses and pointers
Pointer arithmetic.
Class variables and class functions
Operator overloading.
Dynamic allocation of arrays
Templates.
Dynamic memory allocation
Insertion sort.
Sorted arrays.
Sorting algorithms.
Issues with classes.
Dangling pointers.
Dynamic allocation of classes
Counting sort.
Selection sort.
Searching and sorting arrays
Protecting pointers.
Data structures: class
An array class: constructor and destructor
Constructors.
This.
Recursive binary search
Presentation transcript:

Dynamically allocating arrays

Outline In this lesson, we will: See that pointers to instances and arrays are identical See how to allocate and deallocate arrays of data Learn how to use initialization and understand the costs Learn what happens when allocations fail

Arrays and pointers Recall that Output: array: 0x7ffcd75dccb0 Arrays are assigned addresses Pointers can be assigned addresses #include <iostream> int main(); int main() { int array[10]; int *p_value{new int{42}}; std::cout << "array: " << array << std::endl; std::cout << "pointer: " << p_value << std::endl; delete p_value; p_value = nullptr; return 0; } Output: array: 0x7ffcd75dccb0 pointer: 0x11c5010

Arrays and pointers If pointers and addresses are just variables assigned addresses, how does the programming language differentiate between them? Answer: It doesn’t, period. Any address can be interpreted as either: The address of an instance of the given type, or The address of the first entry of an array of a given type

Arrays and pointers Of course, it makes a difference how you use them, but C++ doesn’t care—it’s your problem: #include <iostream> int main(); int main() { int array[4]{150, 108, 250, 406}; std::cout << "*array == " << *array << std::endl; return 0; } Output: *array == 150

Arrays and pointers Of course, it makes a difference how you use them, but C++ doesn’t care—it’s your problem: #include <iostream> int main(); int main() { int *p_value{new int{42}}; std::cout << "p_value[0] == " << p_value[0] << std::endl; std::cout << "p_value[1] == " << p_value[1] << std::endl; std::cout << "p_value[2] == " << p_value[2] << std::endl; delete p_value; p_value = nullptr; return 0; } Output: p_value[0] == 42 p_value[1] == 0 p_value[2] == 0

Arrays and pointers I tried this on ecelinux; your results may differ #include <iostream> int main(); int main() { int *p_value{new int{42}}; for ( std::size_t k{0}; k < 100000; ++k ) { std::cout << "p_value[" << k << "] == " << p_value[k] << std::endl; } delete p_value; p_value = nullptr; return 0; Output: p_value[0] == 42 p_value[1] == 42 ⋮ p_value[33787] == 0 Segmentation fault (core dumped)

Arrays and pointers You run out of memory quickly going the other direction: #include <iostream> int main(); int main() { int *p_value{new int{42}}; for ( int k{0}; k < 100000; ++k ) { std::cout << "p_value[" << k << "] == " << p_value[k] << std::endl; } delete p_value; p_value = nullptr; return 0; Output: -bash-4.2$ ./a.out p_value[0] == 42 p_value[-1] == 0 p_value[-2] == 33 p_value[-3] == 0 p_value[-4] == 0 Segmentation fault (core dumped)

Arrays and pointers If arrays and pointers are interchangeable, how do we keep track? It’s up to you…the programmer Naming conventions help: the identifier for: A local variable or non-array parameter should describe something singular in number control_point A pointer to a single instance should also be prefixed with p_ p_control_point A local array should be plural or contain a word such as array or table control_points control_pt_array A pointer to an array should also be prefixed with an a_ a_ontrol_points

Dynamic allocation of arrays To request an array of integers initialized to their default value: #include <iostream> int main(); int main() { std::size_t capacity{10}; int *a_values{new int[capacity]{}}; for ( std::size_t k{0}; k < capacity; ++k ) { std::cout << a_values[k] << ", "; } std::cout << "..." << std::endl; delete[] a_values; a_values = nulltpr; return 0; Output: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...

Dynamic allocation of arrays The critical differences from allocating a single instance include: int *a_values{new int[capacity]{}}; // Use 'a_values'... // We need a larger array... delete[] a_values; capacity *= 2; avalues = new int[capacity]; // Use the larger 'a_values' array... a_values = nullptr;

Dynamic allocation of arrays To give at least some of the array entries specific values: #include <iostream> int main(); int main() { std::size_t capacity{10}; int *a_values{new int[capacity]{6, 5, 4, 3, 2, 1}}; for ( std::size_t k{0}; k < capacity; ++k ) { std::cout << a_values[k] << ", "; } std::cout << "..." << std::endl; delete[] a_values; a_values = nullptr; return 0; Output: 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, ...

Dynamic allocation of arrays One can also request an array without initialization: #include <iostream> int main(); int main() { std::size_t capacity{10}; int *a_some_array{new int[capacity]{6, 5, 4, 3, 2, 1}}; delete[] a_some_array; int *a_values{new int[capacity]}; for ( std::size_t k{0}; k < capacity; ++k ) { std::cout << a_values[k] << ", "; } std::cout << "..." << std::endl; delete[] a_values; a_values = nullptr; return 0; Output: 0, 0, 4, 3, 2, 1, 0, 0, 0, 0, ...

Uninitialized arrays The significant difference between initialized and uninitialized arrays is the time factor: int *a_values{new int[100000]}; // Do something with 'a_values' delete[] a_values; a_values = nullptr; The operating system only has to find 400 000 bytes It can allocate 400 000 bytes as easily as it can 4 bytes Initialization to a default values requires 100 000 assignments This is potentially expensive with respect to time This is especially true if you are going to assign the entries other values, anyway

Uninitialized arrays These two are equivalent: Explicitly performing the initialization int *a_uninitialized{new int[100000]}; for ( std::size_t k{0}; k < 100000; ++k ) { a_uninitialized[k] = 0; } // Do something with 'a_uninitialized' Having the compiler do the initialization int *a_initialized{new int[100000]{}}; // Do something with 'a_initialized'

Failures in allocation Suppose you request more memory than the operating system can allocate—for example, 1 GiB Remember, the memory must be contiguous The default behavior is to throw a std::bad_alloc exception: int *a_data{nullptr}; try { a_data = new int[capacity]; } catch ( std::bad_alloc e ) { // Do something in case of an allocation failure }

Failures in allocation Alternatively, it is possible to force new to simply return nullptr if memory is not available: int *a_data{new(nothrow) int[capacity]}; if ( a_data == nullptr ) { // Do something in case of an allocation failure } This would be used in an embedded system where the mechanism of try-catch is unnecessarily expensive Again, we will be re-examining this behavior later

Important notice… If you ever adopt the mantra that “arrays and instances are the same so treat them the same”, you will be entering a state of sin int *p_datum{new int{42}}; p_datum[0] = 91; std::cout << p_datum[0] << std::endl; delete p_datum; p_datum = nullptr; If you ever do this: Don’t acknowledge you ever attended Waterloo – Don’t embarrass and humiliate your peers and diminish their degrees You don’t know me, you’ve never seen me

Summary Following this lesson, you now Understand that any pointer can reference an instance or an array Know that arrays are Allocated using new typename[capacity] Deallocated using delete[] Dynamically allocated arrays can be initialized like local arrays Exceptions can be thrown if there is a failure in allocation This can be overloaded to return nullptr

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.