Dynamically allocating arrays within structures

Slides:



Advertisements
Similar presentations
Recursive binary search
Advertisements

For loops.
Templates.
Introduction to classes
Static variables.
Default values of parameters
Pointers.
Reference variables, pass-by-reference and return-by-reference
Dynamically allocating arrays
Binary search.
Do-while loops.
Command-line arguments
Pointer arithmetic.
Console input.
Dangling pointers.
This.
Sorted arrays.
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.
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
Selection sort.
Insertion 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.
The std::string class.
Dynamic allocation of arrays
Templates.
This.
Dynamic memory allocation
Insertion sort.
Sorted arrays.
Sorting algorithms.
Issues with classes.
Dangling pointers.
Dynamic allocation of classes
Encapsulation.
Destructors.
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 within structures

Outline In this lesson, we will: See how to allocate memory in data structures Learn that this should be done within functions Look at how to use such an array

Structures and arrays Recall that it is possible to declare a structure with a fixed-size array struct coordinate_t { double point[3]; }; It is not possible to declare a structure with an array capacity that is not known at compile time: double point[n];

Dynamically allocated arrays If the array size is not known at compile time, the only means of allocating that memory is through dynamic allocation Previously, we saw that we had to always pass around both: The address of an array, and Its capacity This is tedious, and error-prone, so let us create an array data structure

Arrays and pointers Consider this data structure: struct array_t { std::size_t capacity; double *a_entries; }; When we declare such a data structure, we should immediately initialize both its entries: array_t my_array{0, nullptr}; This avoids the possibility of a wild pointer

Arrays and pointers In order to initialize this data structure, we must assign to the entries We will do this in a function: int main() { array_t data{0, nullptr}; // Allocate memory for the array and set all entries to 0.0 data.capacity = 14; data.a_entries = new double[data.capacity]; for ( std::size_t k{0}; k < data.capacity; ++k ) { data.a_entries[k] = 0.0; } // Do something with this data... return 0;

Arrays and pointers Because we will perform all of these steps repeatedly, it is better to convert this to a function call: void init_array( array_t &array, std::size_t array_capacity, double initial_value = 0.0 ) double initial_value ) { array.capacity = array_capacity; array.a_entries = new double[array.capacity]; for ( std::size_t k{0}; k < array.capacity; ++k ) { array.a_entries[k] = initial_value; } The default value is in the function declaration

Arrays and pointers This initialization function simplifies our program: int main() { array_t data{0, nullptr}; init_array( data, 14 ); // Use the default value of 0.0 // Do something with this data... return 0; }

Arrays and pointers Here is another function that uses this array data structure: double array_average( array_t const &array ); double array_average( array_t const &array ) { if ( array.capacity == 0 ) { return 0.0; } else { double sum{0.0}; for ( std::size_t k{0}; k < array.capacity; ++k ) { sum += array.a_entries[k]; } return sum / array.capacity;

Arrays and pointers Here is another: double array_standard_deviation( array_t const &array ); double array_standard_deviation( array_t const &array ) { if ( array.capacity == 0 ) { return 0.0; } else { double sum_squares{0.0}; double average{array_average( array )}; for ( std::size_t k{0}; k < array.capacity; ++k ) { double diff{array.a_entries[k] - average}; sum_squares += diff*diff; } return std::sqrt( sum_squares / array.capacity );

Arrays and pointers We can now declare an instance of this class and use it: int main() { array_t data{0, nullptr}; init_array( data, 15 ); for ( std::size_t k{0}; k < data.capacity; ++k ) { std::cout << "Enter a number: "; std::cin >> data.a_entries[k]; } std::cout << "The average is " << array_average( data ) << std::endl; return 0;

Arrays and pointers Question: What happens when this instance goes out of scope? int main() { array_t data{0, nullptr}; init_array( data, 15 ); for ( std::size_t k{0}; k < data.capacity; ++k ) { std::cout << "Enter a number: "; std::cin >> data.a_entries[k]; } std::cout << "The average is " << array_average( data ) << std::endl; return 0;

Arrays and pointers When an instance of a data structure goes out of scope, the compiler simply ensures that the memory for the member variables is available for future computations That is, only the memory for the capacity and the pointer are reclaimed Consequently, we have the likelihood of a memory leak…

Arrays and pointers To avoid a memory leak, it is necessary to free up the dynamically allocated memory immediately before the array goes out of scope: double collect_data() { array_t data{0, nullptr}; init_array( data, 15 ); for ( std::size_t k{0}; k < data.capacity; ++k ) { std::cout << "Enter a number: "; std::cin >> data.a_entries[k]; } double average{array_average( data )}; delete[] data.a_entries; data.a_entries = nullptr; data.capacity = 0; return average;

Arrays and pointers As we will be doing these three steps again and again, it is preferable to define a function: void destroy_array( array_t &array ); void destroy_array( array_t &array ) { delete[] array.a_entries; array.a_entries = nullptr; array.capacity = 0; }

Arrays and pointers As we will be doing these three steps again and again, it is preferable to define a function: double collect_data() { array_t data{0, nullptr}; init_array( data, 15 ); for ( std::size_t k{0}; k < data.capacity; ++k ) { std::cout << "Enter a number: "; std::cin >> data.a_entries[k]; } double average{array_average( data )}; destory_array( data ); return average;

Resizing the array Suppose we want to resize the array: We must Allocate a new array of the appropriate size For this, we will need a temporary local variable Copy over the data to the new array Delete the old array Update the member variables

Resizing the array Suppose we want to resize the array: void resize_array( array_t &array, std::size_t new_capacity, double initial_value ) { if ( new_capacity != array.capacity ) { double *a_new_entries{ new double[new_capacity] }; std::size_t minimum{std::min( array.capacity, new_capacity )} for ( std::size_t k{0}; k < minimum; ++k ) { a_new_entries[k] = array.a_entries[k]; } for ( std::size_t k{minimum}; k < new_capacity; ++k ) { a_new_entries[k] = initial_value; delete[] array.a_entries; array.a_entries = a_new_entries; a_new_entries = nullptr; array.capacity = new_capacity;

Summary Following this lesson, you now Understand how to allocate memory for data structures Know how to allocate and deallocate such memory It is best to use functions, as this is a repeated operation Resizing an array is possible, but requires careful assignments Local variables going out of scope do not destroy that memory

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.