
Every variable lives somewhere: a numbered byte, or two, in the Einstein's 64K. A pointer is a variable that holds one of those numbers, and C lets you take the number, keep it, pass it about, do arithmetic on it, and go to it. That is the feature the language is famous for, for better and worse. This section is the better: it explains three things you have already met without explanation - why scanf wanted &n, why a function can change an array but not an int, and what an array's name actually is - and it ends with the Einstein's memory laid open, which S25 walks into.
int x, *p;
x = 5;
p = &x;
&x is the address of x - the number of the place where its two bytes are. On this run it was 58413, which is up near the top of memory where the compiler keeps the variables of running functions. p is declared with a *: int *p reads as "*p is an int", that is, p is a thing which, followed, gives an int. A pointer to int. Pointers of every kind are two bytes here, because an address is.
*p is what p points at. Read, it is the value of x; written, it changes x:
*p = 7;
and x is now 7. Nothing was said about x in that line. That is the whole trick, and everything else follows from it.
scanf Wanted &n, And How exchange WorksS13 said a function gets copies, and cannot change the caller's variables. Give it addresses instead, and it can:
exchange(a, b) int *a, *b;
{
int keep;
keep = *a;
*a = *b;
*b = keep;
}
exchange(&x, &y) passes the addresses of x and y; inside, *a and *b are x and y themselves, and afterwards they have swapped. That is what scanf("%d", &n) was doing: it needed to put a value into n, so it needed to know where n was.
(This function is called exchange because the obvious name is taken: the compiler's own library has a swap, and naming yours the same gets duplicate declaration - storage class mismatch, as S13 warned.)
Add 1 to a pointer and it moves to the next thing of its type, not the next byte. p at 58413 pointing at an int: p + 1 is 58415. A char * at 58405: + 1 is 58406. That is what makes a pointer walk an array:
s = "Einstein";
while (*s)
printf("%c-", *s++);
s is a char * pointing at the first letter. *s++ gives the letter and then moves s on by one char; the loop stops when *s is the 0 that ends every string (S17). Output: E-i-n-s-t-e-i-n-.
And now the secret of arrays. An array's name is the address of its first element, and a[i] means *(a + i). So s[2] and *(s + 2) are the same n; so a function given an array is given its address and can change what is there (S15); and so p[1] works on any pointer, not only on something declared as an array. The two notations are one thing, and you may use whichever reads better.
Sometimes you know the address. The screen, the sound chip, a byte the operating system keeps: S24 and S25 have real ones. For now, 7000h, which nothing is using. Turning a number into a pointer takes a cast, and this compiler writes one its own way:
typedef char *char_ptr;
char *p;
p = cast(char_ptr) 0x7000;
*p = 42;
Two things to notice. p = 0x7000; on its own is refused - bad type combination - because a number is not a pointer and the compiler will not quietly pretend it is. And the cast is spelt cast(name), where name must be a typedef name (S10): (char *) 0x7000, the form you may know, gets expecting a primary here. So the typedef line comes first, and then cast(char_ptr) turns the number into a pointer to char, and *p = 42 puts 42 into byte 7000h of the Einstein.
There is a simpler way for a single byte, and the library has it: poke(0x7002, 44) writes a byte at an address and peek(0x7002) reads one. The listing does both and checks they agree with p[2]. Pointers are for when you want to walk through memory; peek and poke are for one byte at a known place, and S25 uses them on the machine itself.
#include STDIO.H
typedef char *char_ptr;
main()
{
int x, *p;
char *s, *m;
x = 5;
p = &x;
printf("x is %d, at %u\n", x, p);
*p = 7;
printf("x is now %d\n", x);
printf("p + 1 is %u\n", p + 1);
s = "Einstein";
printf("%c %c %c\n", *s, *(s + 1), s[2]);
while (*s)
printf("%c-", *s++);
printf("\n");
m = cast(char_ptr) 0x7000;
*m = 42;
m[1] = 43;
poke(0x7002, 44);
printf("%d %d %d %u\n", *m, m[1], peek(0x7002), m);
}
#include ?STDIO.LIB?
Addresses are printed with %u: they go up to 65535, and %d would show the top half of memory as negative (S9).
Starting from
Typed as PTR.C; compiled and run.
What you should see
x is 5, at 58413
x is now 7
p + 1 is 58415
E i n
E-i-n-s-t-e-i-n-
42 43 44 28672
The address of x may differ by a few bytes from run to run of a program you have edited; 28672 is 7000h and will not.
s = "Einstein" to s = "Tatung Einstein". Does the loop need changing?m = cast(char_ptr) 0x7000; to m = 0x7000;. What does the compiler say? Then try m = (char *) 0x7000;.p as char *p instead of int *p, leave p = &x, and see what the compiler says. Then change x to a char as well and see what p + 1 becomes.16.1 Write both(a, b) that takes the addresses of two ints and sets each to the sum of the two. Show it working.
16.2 Using a pointer and *s++, count the characters in a string without calling strlen.
16.3 Fill bytes 7000h to 700Fh with 0 to 15 through a pointer, then read them back with peek and print them in a row.
Worked solutions are in Appendix II.
| Symptom | Cause |
|---|---|
bad type combination at p = number | A number is not a pointer. cast(typedef_name) it. |
expecting a primary here at (char *) | Casts are cast(name) here, with a typedef name. |
need a type name at cast(char *) | The name in cast() must be a typedef name, not a type. |
A function given &x did not change x | It assigned to a instead of *a. |
duplicate declaration - storage class mismatch at your swap | The name is the library's. Call it exchange. |
| A pointer you never set points somewhere and the program went mad | Locals start with rubbish. Set it. |
| Addresses print as negative numbers | %d. Use %u. |
&x is the address of x; int *p holds one; *p is what it points at, to read or write. p + 1 is the next int, or char, whichever p points at; a[i] is *(a + i), and an array's name is its address. A function given addresses can change what they point at. A number becomes a pointer only through cast(typedef_name); peek and poke read and write one byte.
S17, Strings - an array of char with a 0 at the end, the library functions that work on one, and the mistake that S15's crash was really about.