Learning objectives
After this chapter you should be able to:
Do unaided:
- Draw box-and-arrow diagrams of variables, pointers, arrays and pointers to pointers.
- Predict
&x,*p,p + 1(in elements and in bytes),*p++,(*p)++,*++pand++*p. - Write functions with out-parameters, including
swapand one with two out-parameters that reports failure. - Explain array decay and why
sizeof(arr)changes inside a function, and pass a length instead. - Read memory in gdb with
pandx/, and say whichconstplacement protects the pointer and which protects the data.
May delegate:
- Explanations of pointer snippets from the tutor, which you confirm step by step in gdb before accepting them.
- Predict-the-output drills, generated without their answers.
Opening case
A composite case, not a real student or course.
Sam's lab task was small: add up a day's temperature readings so a later function can average them. Sam walked a pointer along the array, as the lab notes suggested. The six readings came from sensor 1001, copied by hand into an array in main. Sam wrote the loop in ten minutes, tested it once and moved on to the averaging function, the part the lab would grade.
The chapter continues from here.
The loop in this chapter
Open a step to see what it asks of you and how it plays out in the Tutor phase.
Predictten predict-the-output items on paper, with confidences.
Step 1 of 6 · You, no AI
What you hand in: A written prediction: the output, the memory state, the operation count, and how sure you are.
Why this step: A prediction written before the run is the only way to learn whether your model of the machine is right. A miss is the most useful result there is, because it shows exactly where the model is wrong.
The first prediction is never delegated.
Ask yourself: What will this print, what will memory look like when it ends, and how many operations will it take?
Log the prediction in your work log · Predict in every chapter · ← Predict in Chapter 3 · Predict in Chapter 5 →
Write
swapand a function with two out-parameters (stn_mean_sdfor PA1), by hand.Step 2 of 6 · You, no AI
What you hand in: One small piece written by hand, to build the mental model.
Why this step: You can only check what you could have built. Writing a small piece yourself gives you the model you will need to judge the agent's version later.
Ask yourself: Could I write this piece again tomorrow without notes?
Browse the companion code · Write in every chapter · ← Write in Chapter 3 · Write in Chapter 5 →
Specifya question to the tutor about one wrong prediction, with the run that refuted it.
Step 3 of 6 · You
What you hand in: The header or class interface, unit tests, a complexity budget and the ownership rules.
Why this step: The specification is the contract the code will be held to. Tests written first, a cost budget and ownership rules turn "looks right" into something you can check.
Tutor In the Tutor phase you specify a question instead of code: what you want explained, quizzed or drilled.
The ownership and lifetime section of a specification is never delegated.
Ask yourself: Who allocates, who frees, what does it cost, and what input would break it?
Prompt and specification templates · Specify in every chapter · ← Specify in Chapter 3 · Specify in Chapter 5 →
Delegatethe tutor explains as numbered claims and writes no code you submit.
Step 4 of 6 · The agent
What you hand in: The implementation from your specification, or an explanation in the Tutor phase, with the prompts and transcript saved.
Why this step: The agent does the typing; you keep the judgment. Saving the prompts and the transcript makes the delegation something you can audit afterward.
Tutor Tutor phase: the agent explains, quizzes and makes practice problems. It writes no code you submit.
Ask yourself: Did I hand over my specification and tests, and did I save what the agent said?
Prompt templates · Delegate in every chapter · ← Delegate in Chapter 3 · Delegate in Chapter 5 →
Audita gdb memory dump (
p,x/4dw) checks each claim; a known-answer test checks your functions.Step 5 of 6 · You and the tools
What you hand in: The Audit Report: tests, sanitizer and Valgrind output, your own cost derivation against a timing plot, the defects found, and a keep-or-rewrite decision.
Why this step: What the agent says is testimony until an instrument confirms it. Tests, sanitizers, Valgrind and a timing plot are evidence.
Tutor In the Tutor phase the audit checks the tutor's claims as well as your own code.
The final judgment that the code is correct is never delegated.
Ask yourself: What did the sanitizer report, does the timing plot match my derivation, and would I keep this code or rewrite it?
The Audit Report template · Audit in every chapter · ← Audit in Chapter 3 · Audit in Chapter 5 →
Defendteach the confused peer that
sizeof(arr)in a function is a pointer's size, with a run as evidence.Step 6 of 6 · You, live
What you hand in: A five-minute oral defense.
Why this step: If you can explain it live, you own it. The oral checks that the understanding is yours and not the agent's.
Ask yourself: Why this bound, what input breaks it, and where is this memory freed?
How the loop works · Defend in every chapter · ← Defend in Chapter 3 · Defend in Chapter 5 →
What agents get wrong here
Patterns observed in agent-written code, to test for, not guarantees. If the agent no longer makes the mistake, your job is to prove it got it right, with the same instrument.
Reading *p++ as incrementing the value
How to catch it -Werror=unused-value when the result is unused; otherwise a hand trace or gdb
Section 4.3.1 · Compiler warnings, gdb, Your own trace
Computing an array's length with sizeof on a pointer parameter
How to catch it A known-answer test needing more than one element; p n in gdb; read the signature
Section 4.3.2 · gdb, Tests
Dereferencing a result documented to be NULL on failure
How to catch it A test that forces the NULL path; UBSan load of null pointer
Section 4.3.3 · UndefinedBehaviorSanitizer, Tests
What the chapter covers
- 4.1 The concept, by hand
- 4.1.1 Addresses, declaring and dereferencing
- 4.1.2 Null, and pointers you must not follow
- 4.1.3 Out-parameters and swap
- 4.1.4 Pointer arithmetic counts elements
- 4.1.5 Arrays and decay
- 4.1.6 Walking a pointer:
*p++and its relatives - 4.1.7 Pointers to pointers
- 4.1.8
constplacements - 4.1.9 Reading memory in gdb:
pandx/
- 4.2 Predict and check
- 4.3 What agents get wrong here
- 4.3.1 Precedence of
*and++ - 4.3.2
sizeofon a decayed array - 4.3.3 Dereferencing a possibly null pointer
- 4.3.1 Precedence of
- 4.4 Working with the agent
- 4.5 Lab: Check the tutor's trace in gdb
- 4.6 Defend it
Cost thread
Walking a pointer and indexing do the same work (Table 4.4).
Summary
- A pointer holds an address:
&xproduces one,*pfollows one, andpand*pare different objects of different sizes. - C passes by value, so a function changes its caller's variables only through their addresses:
swapand out-parameters, with a status return and null checks. - Pointer arithmetic counts elements:
p + 1addssizeof *pbytes,a[i]is*(a + i), and only pointers up to one past the end are valid. - Arrays decay to pointers at calls, so
sizeof(arr)in a function is a pointer's size and the length must be passed. *p++moves the pointer;(*p)++changes the element. When the result is used both compile, and only a trace, gdb or a sanitizer tells you which you wrote.- gdb's
pandx/4dwsettle claims about memory, including an agent's.
Key terms
- address: the number of a byte in memory.
- pointer: a variable whose value is an address, typed by what lives there.
- dereference: to follow a pointer to the object at its address, written
*p. - null pointer: a pointer value (
NULL) that points to no object. - out-parameter: a pointer parameter through which a function writes a result for its caller.
- pointer arithmetic: adding an integer to a pointer, or subtracting two pointers, measured in elements.
- one past the end: the position after an array's last element, which a pointer may hold but never dereference.
- array decay: the automatic conversion of an array to a pointer to its first element.
- pointer to pointer: a pointer whose target is a pointer, such as
int **pp. - pointer to const: a pointer such as
const int *pthrough which its target cannot be changed. - const pointer: a pointer such as
int *const pthat cannot be made to point elsewhere.
Connections
Where this chapter and another point to each other most often.
- Chapter 1: From Python to C: What Python Was Hiding
Start each session with the tutor prompt from Chapter 1, then use one of three roles. > The tutor explains a snippet after you have written your own prediction. - Chapter 3: Control Flow, Functions, Recursion, and Counting Operations
A local variable's lifetime ends with its block (Chapter 3), but a pointer holding its address keeps the old number. - Chapter 5: Arrays, Strings, and Matrices: n, n², n³
PA1 (begun in Chapter 3, due in Chapter 5) follows this convention; Listing 4.4 is its interface. - Chapter 6: The Stack and the Heap: Dynamic Memory and Ownership
Chapter 6 treats the heap version. ### 4.1.3 Out-parameters and swap C passes every argument by value (Chapter 3): the function gets a copy, and changing the copy leaves the caller's variable alone. - Chapter 12: Moving to C++: Is It Really C++?
Chapter 12 extends the idea to C++. ### 4.1.9 Reading memory in gdb:pandx/Beyond Chapter 3'sbreak,run,nextandstep, two commands check every claim in this chapter against the running program.
Further reading
- Kernighan and Ritchie (1988), Chapter 5: pointers and arrays from the language's designers; still the clearest account of
a[i]as*(a + i). - ISO/IEC (2018), Sections 6.3.2.1, 6.5.6 and 6.7.6.3: decay, pointer arithmetic and array parameters, as the standard states them.
- Serebryany et al. (2012): how AddressSanitizer guards the bytes around each object, which explains the
[48, 96)in the opening case.