By the end of this lesson, cadets will be able to:
p + 1 advances the pointer by sizeof(*p) bytes. (Outcome 2)a[i] == *(a + i) to read and write code interchangeably in either notation. (Outcome 2)*p++, *(p++), and (*p)++. (Outcome 2, 3)Complete before class. Today's reading is short but precise; reread anything that does not sit right.
| Source | Sections | Why |
|---|---|---|
| Beej-C | Chapter 11, sections 11.1 and 11.2 | Pointer arithmetic and array-pointer equivalence. Required. |
| Beej-C | Chapter 11, section 11.3 (void pointers) | Optional skim. We cover void* formally at Lesson 36 with function pointers. |
sizeof the pointed-to type, not by 1 byte.a[i] and *(a + i) are the same expression. The compiler treats them identically.int *p; declares.&x (address of) and *p (dereference).NULL is a valid value for a pointer and what dereferencing it does.void * at the end of this chapter. You can skim it now, but we do not use it until Lesson 36 (Function Pointers). If you are short on time, skip it.Today's content is a short list but a deep one. Three rules drive everything: pointers move in units of the type they point to, the precedence of postfix ++ binds tighter than the dereference, and arrays and pointers are interchangeable in expressions. Internalize those three and most of the C standard library starts to make sense.
The Lesson 13 deck covers pointer arithmetic, the precedence trio, array-pointer duality, and pointer subtraction. The slides are the spine of the in-class lecture; the walkthrough widget is where you practice each idea by hand.
A pointer is a variable whose value is the address of another variable. int *p = &x; stores the address of x in p. Dereferencing with *p reads or writes the value at that address. Today we ask: what if you do arithmetic on the address itself?
You can add an integer to a pointer or subtract one from it. The result is a new pointer that has moved through memory. The amount of movement is not what you might expect.
Notice the address in p did not increase by 1 byte when we wrote p + 1. It increased by sizeof(int) bytes (4 on our lab machines). The pointer's type is part of its identity; the compiler uses that type to decide how far one step is.
The byte distance moved by + 1 depends entirely on the pointer's declared type:
| Pointer type | Bytes per step | Example: if p == 0x1000, p+1 == |
|---|---|---|
| char * | 1 | 0x1001 |
| short * | 2 | 0x1002 |
| int * | 4 | 0x1004 |
| double * | 8 | 0x1008 |
| long * | 8 | 0x1008 |
This is why pointer arithmetic is useful instead of error-prone: you do not have to remember how big each type is. The compiler handles the multiplication for you. p + n always lands on the n-th element after p, regardless of the underlying type.
*p++, *(p++), (*p)++These three expressions look almost identical. Two of them are the same; the third is completely different. Read the precedence callout, then study the table.
++ sits with () and [] at level 1, the highest. Prefix * (dereference) sits at level 2.*p++ always parses as *(p++) — the ++ binds to p, not to *p. To increment what p points to, you must write (*p)++.| Expression | Parses as | What it does |
|---|---|---|
| *p++ | *(p++) | Reads the value at p, then advances p by one element. The expression's value is the original *p. |
| *(p++) | *(p++) | Identical to *p++. The parentheses are documentation, not behavior. Use them when you want a reader to see the precedence at a glance. |
| (*p)++ | (*p)++ | Dereferences p first, then increments the value being pointed to. p itself does not move. |
Concrete example. Suppose int arr[3] = {10, 20, 30}; and int *p = arr;. Each row below starts fresh from that state:
Here is the formula that ties this lesson together:
The compiler treats these two expressions identically. Both are valid C; you can use either notation on an array, and you can use either notation on a pointer that points into an array. The array name itself acts like a pointer to its first element in most expressions.
There is one important asymmetry. You can modify a pointer; you cannot modify an array variable. p++ works. arr++ is a compile error. The array name is a fixed label for a fixed block of memory; the pointer is a variable that can be aimed anywhere.
This duality is also why functions like strlen(char *s) and strlen(char s[]) are equivalent declarations. When you pass an array to a function, the function receives a pointer to its first element. The length information does not come along; you pass it separately or you put a sentinel like '\0' at the end of the data.
Subtracting an integer from a pointer works like addition: the pointer moves backward by that many elements. More interesting, you can subtract two pointers that point into the same array. The result is the number of elements between them, not the number of bytes.
The compiler divides the byte difference by sizeof the pointed-to type for you. This is consistent with pointer addition: both operations work in units of elements, not bytes.
Subtracting pointers that do not point into the same array is undefined behavior. The compiler will not stop you, but the result is meaningless.
Three things to do in class. Use the checklist to track your progress.
Twelve tasks across five sections. Type each C statement at the prompt. The terminal shows what the simulator did; the memory pane shows variables, an array, and an arrow from each pointer to the address it currently points at.
Enter your name. It will appear on the completion screen so you can include it in your screenshot for the Blackboard reflection.
Five questions covering the precedence trio, type-aware stepping, and the array-pointer duality formula. Answer each one and click Check to see how you did. The final summary tells you whether you are ready to move on to the lab.
Enter your name. It will appear on the score card so you can include it in your screenshot for the Blackboard reflection.
char * over a different array and see how the byte steps change.