05 Pointers Post BW
05 Pointers Post BW
int main(void) {
printf("the value of g is: %d\n", g);
printf("the address of g is: %p\n", &g);
}
the value of g is: 42
the address of g is: 0x725520
The type of a pointer is the type of memory address it can store (or
“point at”).
int i = 42;
char c = 'z';
struct posn p1 = {3, 4};
int i = 42;
int *p1 = &i; // pointer p1 points at i
int **p2 = &p1; // pointer p2 points at p1
trace_int(i);
trace_ptr(&i);
trace_ptr(p);
trace_ptr(&p);
i => 42
&i => 0xf020
p => 0xf020
&p => 0xf024
i int 0xf020 42
p int * 0xf024 0xf020
if (p) ...
if (p != NULL) ...
sizeof(int *) ⇒ 8
sizeof(char *) ⇒ 8
i = 42;
p = &i;
trace_int(*p);
*p => 42
*p ⇒ 42
p = NULL;
trace_int(*p); // crash!
struct posn {
int x;
int y;
};
int main(void) {
struct posn my_posn = {3, 4};
struct posn *ptr = &my_posn;
trace_int((*ptr).x) // awkward
trace_int(ptr->x); // much better
int *p = &i;
int *q = &j;
p = q;
int *p = &i;
int *q = &j;
the statement
*p = *q;
does not change the value of p: it changes the value of what p
points at. In this example, it changes the value of i to 6, even
though i was not used in the statement.
int i = 1;
int *p1 = &i;
int *p2 = p1;
int **p3 = &p1;
trace_int(i);
*p1 = 10; // i changes...
trace_int(i);
*p2 = 100; // without being used directly
trace_int(i);
**p3 = 1000; // same as *(*p3)
trace_int(i);
i => 1
i => 10
i => 100
i => 1000
int main(void) {
int x = 5;
inc(x);
trace_int(x); // 5 or 6 ?
}
The inc function is free to change its own copy of the argument (in
the stack frame) without changing the original variable.
int main(void) {
int x = 5;
trace_int(x);
inc(&x); // note the &
trace_int(x);
}
x => 5
x => 6
int main(void) {
int a = 3;
int b = 4;
trace_int(a); trace_int(b);
swap(&a, &b); // Note the &
trace_int(a); trace_int(b);
}
a => 3
b => 4
a => 4
b => 3
• produce output
• read input
• mutate a global variable
• mutate a variable through a pointer parameter
// effects: modifies *px and *py
void swap(int *px, int *py) {
int temp = *px;
*px = *py;
*py = temp;
}
int main(void) {
int x = 3;
int y = 4;
int *bad_idea(int n) {
return &n; // NEVER do this
}
int *bad_idea2(int n) {
int a = n * n;
return &a; // NEVER do this
}
In this course only read in one value per scanf. This will help
you debug your code and facilitate our testing.
if (retval != 1) {
printf("Fail! I could not read in an integer!\n");
}
int read_sum(void) {
int sum = 0;
int n = 0;
while (scanf("%d", &n) == 1) {
sum += n;
}
return sum;
}
int read_int(void) {
int i = 0;
int result = scanf("%d", &i);
if (result == 1) {
return i;
}
return READ_INT_FAIL;
}
We “receive” two values: the return value, and the value read in
(stored in i).
This function performs division and “returns” both the quotient and
the remainder.
void divide(int num, int denom, int *quot, int *rem) {
*quot = num / denom;
*rem = num % denom;
}
For structures, the entire structure is copied into the frame. For large
structures, this can be inefficient.
struct bigstruct {
int a;
int b;
int c;
int d;
...
int y;
int z;
};
*p = 10; // INVALID
i = 10; // still valid
However, the pointer variable itself is still mutable, and can point to
another int.
p = &j; // valid
*p = 10; // INVALID
The rule is “const applies to the type to the left of it, unless it’s
first, and then it applies to the type to the right of it”.
Because a copy of the argument is made for the stack, it does not
matter if the original argument value is constant or not.
1. “Pure” function
No side effects or dependencies on global mutable variables.
The type of a function pointer includes the return type and all of the
parameter types, which makes the syntax a little messy.
int main(void) {
int (*fp)(int, int) = NULL;
fp = my_add;
trace_int(fp(7, 3));
fp = my_sub;
trace_int(fp(7, 3));
}
fp(7, 3) => 10
fp(7, 3) => 4
Because functions are not “first class values” C cannot get the
“value” of a function. Instead it uses the address of the function.
int sqr(int i) {
return i * i;
}
int main(void) {
io_apply(sqr);
}