ENGGEN 131 Chap.9 Pointers, Parameter Passing and 2D Arrays
Pointers, Parameter Passing and 2D Arrays
This chapter draws pointers as stored addresses leading to typed objects. It separates pointer assignment from dereferenced assignment, explains caller mutation through address parameters and checks target validity and lifetime before every dereference. Two-dimensional arrays are mapped both as logical rows and columns and as contiguous row-major element storage with explicit dimension boundaries.
What this chapter covers
- 01
Address-of and dereference operators
- 02
Pointer value versus target value
- 03
Mutation through address parameters
- 04
Null, uninitialised and stale pointers
- 05
Row-major two-dimensional array layout
Worked example · free
Swap two values through pointers
- 1Save the first target value 12 in a temporary.
- 1Write the second target value 5 into a.
- 1Write the saved 12 into b, giving a=5 and b=12.
Key terms
- Pointer
- A typed variable whose stored value is an address.
- Dereference
- Following a pointer to access the target object.
- Row-major order
- An array layout that stores one complete row before the next.
Pointers, Parameter Passing and 2D Arrays FAQ
Does C pass arguments by reference?
C passes values. Passing an address copies the pointer value, and both the caller and function can then identify the same target object. Rehearse this distinction with a fresh object and pointer-lifetime trace before relying on program output.
What must be true before dereferencing?
The pointer must identify a live object of a compatible type and must not be null or uninitialised. Test the claim on a boundary case where the object and pointer-lifetime assumptions become visible.
How do I bound nested array loops?
Use the row count for valid row indices and the column count for valid column indices, keeping each loop below its corresponding count. Write the expected object and pointer-lifetime state first, then compare it carefully with execution.
Exam move
Draw storage before evaluating pointer expressions. Represent each live object as one box containing its current value and each pointer object as a separate box containing an arrow. Label pointer type and target. Assignment to a pointer changes the arrow; assignment through a dereference changes the target box. When two pointers alias, draw two arrows to one box rather than duplicating the target.
This diagram makes later reads reflect earlier writes and prevents parameter names from being mistaken for separate caller objects. Before every dereference, perform a three-part validity check: the pointer has a known target, the target type is compatible and the target lifetime is still active. An uninitialised pointer has no established target.
A null pointer deliberately represents no target and may be compared but not followed. A pointer to an ordinary local variable becomes stale after that function returns. A numerical address printed by a debugger cannot establish these semantic facts, so pair it with provenance and lifetime. Trace pointer parameters as pass-by-value.
The called function receives copies of addresses, and those copies can identify caller-owned objects. Reassigning a local pointer changes only its local address value, while writing through it can modify the caller’s target. For a swap, use columns for each pointer address and each target value. Show that the arrows remain aimed at the same objects while the object contents exchange.
This explanation is more precise than saying that C passes the integers by reference. For two-dimensional arrays, write row count, column count and legal subscript ranges. Ordinary arrays use row-major layout, but contiguous storage does not erase logical boundaries. A two-by-three array has rows zero and one and columns zero through two; column three remains invalid even if a simplistic offset seems to reach the next row.
A function needs enough inner-dimension information to calculate row steps. Match each nested-loop boundary to the dimension it controls. Practise alias and lifetime counterexamples. Let two pointers identify one integer, write through one and predict the read through the other. Contrast that with two distinct integers holding equal values.
Consider a pointer returned to caller-owned storage and a pointer returned to a completed local object, explaining why only the first remains valid. For two-dimensional traversal, test the last legal element and first rejected row and column. State the object reached by every legal expression. Finish with a memory audit table containing expression, pointer value, target object, target lifetime and effect.
Apply it to a mutation function and a two-dimensional array function. When diagnosing, classify the first invalid step as uninitialised, null, dangling, wrong indirection level, incompatible type or out-of-bounds subscript. Mastery means proving that each dereference reaches one intended live object and that each row and column access stays within its declared dimensions.
End with a combined memory diagram for a two-by-three array and two pointer parameters that may alias one element. Label every live object, stored address, row and column bound. Trace an assignment to a pointer, a write through it and a read through the alias, recording what changes after each statement. Then traverse the matrix with nested loops and list the final legal and first rejected indices for both dimensions.
Add a null case and a pointer to an expired local object, explaining why neither may be dereferenced even if a debugger displays an address. This integrated audit checks pass-by-value addresses, target mutation, aliasing, lifetime, row-major structure and logical bounds with one consistent object map.