A three-dimensional array uses three indexes to identify a value. A grid application can give those indexes the meanings property, row, and column. The storage is only useful if allocation, updates, display, expansion, and cleanup all agree on that order.
Review CS1: grids and flat indexes and CS1: allocation lifetime. This lab combines grid state with nested ownership. The examples describe a representation and test strategy rather than a private assignment implementation.
Define what each coordinate means #
Suppose the first property plane describes a category and the second describes a quantity at the same location. Then field[0][r][c] and field[1][r][c] belong to one cell: same row, same column, different property.
Write down the dimensions before constructing loops. A two-row, three-column grid has row indexes zero and one and column indexes zero, one, and two. A two-plane property dimension has indexes zero and one. A valid row does not compensate for an invalid column or property index.
The property values also have a relationship. If a category describes what occupies the cell, the quantity should describe that same occupant. Updating one plane while accidentally leaving another plane's previous state can create a logically inconsistent cell even though every subscript is in bounds.
Give empty state an explicit meaning #
A sentinel can represent an empty cell. Decide which state value means empty, and what other properties should contain for an empty cell. The sentinel is a convention for control and interpretation, not an ordinary category to process like a planted entry.
Initialization should establish that convention for every new cell. A growth operation first checks whether a cell is empty; it should not turn empty locations into occupied ones merely by applying arithmetic to every stored number. Planting checks coordinates and whether the location can accept the requested occupant. Clearing restores the empty-state relationship across the relevant property planes.
For a dry run, record a cell as a complete set of properties before and after each operation. That makes a mismatched update visible. Reading only the quantity plane can miss an error in the category plane.
Count the allocations, not just the outer pointer #
A pointer-based representation can allocate an outer array of property-plane pointers. Each plane then receives an array of row pointers. Each row receives an array of actual values. These are distinct allocations, not one magically recursive allocation.
With two planes and two rows per plane, there is one outer allocation, two row-pointer allocations, and four innermost value-array allocations. The column count determines the length of each innermost row. This accounting helps predict the number and order of cleanup operations.
Pointers do not record their allocated lengths. Keep property count, row count, and column count available to every operation that traverses the structure. If row lengths vary, the interface must provide those actual lengths instead of assuming a rectangle.
During construction, do not access a cell until the pointers along its path have been initialized. If allocation fails partway through, release the allocations already completed. A normal-completion cleanup loop alone is not a complete plan for partially constructed nested storage.
Release storage while its addresses are available #
Cleanup proceeds from the inside outward. For each plane and row, delete the innermost value array. Once all rows of a plane are released, delete that plane's row-pointer array. Finally, delete the outer property-plane pointer array.[1]
Deleting only the outer array releases only its own pointer elements. It does not automatically follow those pointers to delete every reachable allocation. Worse, losing those addresses can make the inner allocations unavailable for later cleanup.
After deletion, old references and aliases must not be used. Clearing the top-level owner does not clear every copied row pointer. Each alias's usefulness depended on the lifetime of the actual target storage.
The raw-allocation exercise exposes these obligations. Nested standard containers can provide automatic cleanup in ordinary applications, while the coordinate and state rules still remain the program's responsibility.
Expand by preserving coordinates and properties #
An expansion needs replacement storage large enough for the intended new dimensions. Construct it, initialize its new cells to the agreed empty state, and copy each old cell to the corresponding property, row, and column. Copy every property plane, not just the one easiest to display.
For example, growing a two-row, three-column grid to three rows and four columns creates six additional cell locations. The six old locations retain their original coordinates. Extra columns and the new row start in the initialized empty state. The number of property values increases accordingly for each plane.
Only after replacement construction and copying succeed should the old allocations be released and the owning pointer and dimensions changed to describe the new storage. Updating dimensions too early can make later loops traverse beyond the old allocation. Deleting old storage too early loses the data needed for copying.
If shrinking is permitted, define what happens to cells outside the retained dimensions before copying. Do not silently assume every old coordinate still fits a smaller replacement. Any aliases into released old storage become invalid and must not be reused.
Practice and behavioral checks #
Begin with an empty grid. Display should show emptiness, and ordinary growth should leave empty cells empty. Plant at a valid cell and check every associated property. Try an occupied cell and an out-of-range coordinate and verify that rejected operations leave the grid unchanged.
Test the first and last row and column, including each corner. Expand and compare every old cell's complete state with its pre-expansion state. Then check new cells for the intended empty-state values. Finally, check that cleanup releases each allocation once, in the order that preserves the addresses needed for the remaining releases.