Theory
One slip per kind of house
Last lesson you wrote your first pointer. Today the drill: the marks program has an int marks, a float percentage and a char grade, and each needs its own pointer.
First instinct: "an address is an address, one pointer type should fit all."
C disagrees, with good reason: when the courier reaches the address, he must know whether to pick up 4 bytes or 1, and how to read them. The slip carries a TYPE.
Theory
One pointer per type
int marks = 78; int *pi = &marks;
float perc = 78.5; float *pf = &perc;
char grade = 'A'; char *pc = &grade;
The rule: a pointer's type matches what it points at. Why? Dereferencing must know the box's size and encoding: *pi reads 4 bytes as an int, *pc reads 1 byte as a char. Point a float at an int and the compiler protests, and if forced, pf would read the int's bytes as float nonsense, the costume-mismatch garbage of Unit 1, now at the memory level.
Theory
The parcel size on the slip
An address slip for a godown pickup does not just say WHERE, it says WHAT to collect: "4-byte int crate" or "1-byte char envelope". A courier with the wrong slip at the right address grabs the wrong amount and mangles the goods. And a BLANK slip? That is NULL: an honest "no destination yet", infinitely safer than a slip with a random scribbled address (an uninitialized, wild pointer).
Practical
The full pointer drill
#include <stdio.h>
int main() {
int marks = 78;
float perc = 78.5;
char grade = 'A';
int *pi = &marks;
float *pf = &perc;
char *pc = &grade;
int *safe = NULL; /* points nowhere, on purpose */
printf("%d %.1f %c\n", *pi, *pf, *pc);
if (safe != NULL) printf("never runs\n");
return 0;
}This example runs in Gri-Learn on the web, where you can edit it and see the output.
Quiz
Why must a pointer to a float be declared float* rather than int*?
- Dereferencing uses the type to know how many bytes to read and how to interpret them
- It is only a naming convention; any pointer type works the same
- float* pointers are stored in a special part of memory
- int* pointers cannot hold large addresses
Show the answer
Dereferencing uses the type to know how many bytes to read and how to interpret them
All pointers hold similar-looking addresses; the TYPE is instructions for the dereference: pf reads 4 bytes as IEEE float, pc reads 1 byte as a character. Wrong type = wrong width + wrong interpretation = garbage. "Why are pointer types needed?" is a standing exam short-answer, and this is its full-mark reply.
Theory
NULL and the typeless void*
NULL: the deliberate nowhere. int *p = NULL; says "no target yet", and code can CHECK it: if (p != NULL). Contrast the wild pointer: declared, never initialized, holding a random address, dereference it and the program corrupts or crashes. NULL is checkable; wild is chaos.
*void \vp: the generic slip, accepts ANY type's address (`vp = &marks; vp = &perc;`). The price: it cannot be dereferenced directly*, C does not know the parcel size. Cast first: `(int )vp`. Libraries use void to serve every type with one function.
Think first
Spot the danger
Three pointers: int a = &marks; int b = NULL; int *c; (never assigned). Rank them: which dereference is safe, which is a guaranteed crash you can at least TEST for, and which is the worst?
Show the answer
*a is safe: it targets a real box. *b crashes, but predictably, and code can guard it: if (b != NULL). *c is the worst: a wild pointer holding random garbage; dereferencing might corrupt data silently and "work" until the demo day. Moral printed on every C programmer's wall: initialize pointers, to a target or to NULL, never leave them wild.
Quiz
void *vp = &marks; What is required before reading the int through vp?
- A cast: *(int *)vp, because void* carries no size information
- Nothing: *vp works directly
- vp must first be set to NULL
- void pointers can never be made usable
Show the answer
A cast: *(int *)vp, because void* carries no size information
void stores the WHERE but not the WHAT: no type, no byte-width, so a bare vp is illegal. The cast (int )vp temporarily dresses the address in its real type, and then the dereference knows to read 4 bytes as an int. "Generic but cast-before-use" is void's entire exam story.
Watch out
Where marks leak
Type mismatches: float pf = &marks compiles with warnings on some setups and behaves badly; match types, always. Wild pointers: int p; p = 5; is a lottery of corruption, initialize to a target or NULL. Dereferencing void\* without a cast: compile error, and knowing WHY (no size info) is the mark-earner. Style note: int p and int p both compile; the binds to the NAME (int* a, b declares one pointer and one int, a sneaky classic).
Theory
Next: pointers learn to walk
Here is a teaser for the next lesson: since pi holds an address, what is pi + 1? Not address-plus-one-byte, C is smarter: it steps by one whole box (4 bytes for int, 1 for char), which is exactly what makes pointers and arrays best friends. Pointer arithmetic, next.
Summary
Key takeaways
- Declare per type: int pi, float pf, char *pc; the type must match the target.
- The type tells dereference how many bytes to read and how to interpret them.
- NULL = deliberate nowhere, checkable with if (p != NULL); wild pointers = random chaos.
- Initialize every pointer: to a real target or to NULL, never leave it wild.
- void holds any address but must be cast before dereferencing: (int *)vp.
- Memory hook: the slip names the parcel size; a blank slip beats a scribbled one.