Lecture 8 & 9 generics
Lecture 8 & 9 generics
Learn how to write C code that works with any data type.
Learn about how to use void * and avoid potential pitfalls.
Generics
} b 0xf18 0xff10
swap_int() a 0xf10 0xff14
int main(int argc, char *argv[]) { …
int x = 2;
int y = 5;
swap_int(&x, &y);
// want x = 5, y = 2
printf("x = %d, y = %d\n", x, y);
return 0;
}
Swap
} b 0xf18 0xff10
swap_int() a 0xf10 0xff14
int main(int argc, char *argv[]) { temp 0xf0c 2
int x = 2; …
int y = 5;
swap_int(&x, &y);
// want x = 5, y = 2
printf("x = %d, y = %d\n", x, y);
return 0;
}
Swap
} b 0xf18 0xff10
swap_int() a 0xf10 0xff14
int main(int argc, char *argv[]) { temp 0xf0c 2
int x = 2; …
int y = 5;
swap_int(&x, &y);
// want x = 5, y = 2
printf("x = %d, y = %d\n", x, y);
return 0;
}
Swap
} b 0xf18 0xff10
swap_int() a 0xf10 0xff14
int main(int argc, char *argv[]) { temp 0xf0c 2
int x = 2; …
int y = 5;
swap_int(&x, &y);
// want x = 5, y = 2
printf("x = %d, y = %d\n", x, y);
return 0;
}
Swap
return 0;
}
Swap
0xf '\0'
int main(int argc, char *argv[]) {
char *x = "2"; 0xe '5'
DATA SEGMENT 0xd '\0'
char *y = "5";
swap_string(&x, &y); 0xc '2'
// want x = 5, y = 2 …
return 0;
}
Swap
}
Swap
0xf '\0'
int main(int argc, char *argv[]) {
char *x = "2"; 0xe '5'
DATA SEGMENT 0xd '\0'
char *y = "5";
swap_string(&x, &y); 0xc '2'
// want x = 5, y = 2 …
return 0;
}
Swap
0xf '\0'
int main(int argc, char *argv[]) {
char *x = "2"; 0xe '5'
DATA SEGMENT 0xd '\0'
char *y = "5";
swap_string(&x, &y); 0xc '2'
// want x = 5, y = 2 …
return 0;
}
Swap
0xf '\0'
int main(int argc, char *argv[]) {
char *x = "2"; 0xe '5'
DATA SEGMENT 0xd '\0'
char *y = "5";
swap_string(&x, &y); 0xc '2'
// want x = 5, y = 2 …
return 0;
}
“Awesome! Thanks. We also have
20 custom struct types. Could you
write swap for those too?”
Generic Swap
What if we could write one function to swap two values of any single
type?
Problem: each type may need a different size temp! (modern machine * = 8B)
Generic Swap
It copies the next n bytes that src points to to the location contained in
dest. (It also returns dest). It does not support regions of memory
that overlap.
memcpy must take pointers to the bytes to work with to
int x = 5;
know where they live and where they should be copied to.
int y = 4;
memcpy(&x, &y, sizeof(x)); // like x = y
memmove
It copies the next n bytes that src points to to the location contained
in dest. (It also returns dest).
memmove
1 2 3 4 5 6 7
4 5 6 7 5 6 7
Generic Swap
int x = 2;
int y = 5;
swap(&x, &y, sizeof(x));
Generic Swap
short x = 2;
short y = 5;
swap(&x, &y, sizeof(x));
Generic Swap
char *x = "2";
char *y = "5";
swap(&x, &y, sizeof(x));
Generic Swap
mystruct x = {…};
mystruct y = {…};
swap(&x, &y, sizeof(x));
C Generics
Void * has more room for error because it manipulates arbitrary bytes
without knowing what they represent. This can result in some
strange memory Frankensteins!
Swap Ends
You’re asked to write a function that swaps the first and last elements in
an array of numbers.
You’re asked to write a function that swaps the first and last elements in
an array of numbers.
Let’s write out what some other versions would look like (just in case).
Let’s write a version of swap_ends that works for any type of array.
Let’s write a version of swap_ends that works for any type of array.
Let’s write a version of swap_ends that works for any type of array.
Let’s write a version of swap_ends that works for any type of array.
arr + nelems – 1
arr + nelems – 1
arr + nelems – 1
arr + nelems – 1
arr + nelems – 1
Let’s write a version of swap_ends that works for any type of array.
(nelems – 1) * elem_bytes
Swap Ends
Let’s write a version of swap_ends that works for any type of array.
(nelems – 1) * elem_bytes
Swap Ends
Let’s write a version of swap_ends that works for any type of array.
Let’s write a version of swap_ends that works for any type of array.
You’re asked to write a function that swaps the first and last elements in
an array of numbers. Well, now it can swap for an array of anything!
You’re asked to write a function that swaps the first and last elements in
an array of numbers. Well, now it can swap for an array of anything!
You’re asked to write a function that swaps the first and last elements in
an array of numbers. Well, now it can swap for an array of anything!
You’re asked to write a function that swaps the first and last elements in
an array of numbers. Well, now it can swap for an array of anything!
You’re asked to write a function that swaps the first and last elements in
an array of numbers. Well, now it can swap for an array of anything!
mystruct structs[] = …;
size_t nelems = …;
swap_ends(structs, nelems, sizeof(structs[0]));
Stacks
int_stack *int_stack_create() {
int_stack *s = malloc(sizeof(int_stack));
s->nelems = 0;
s->top = NULL;
return s; How might we modify this function to be
} generic?
new_node->next = s->top;
s->top = new_node;
s->nelems++;
}
new_node->next = s->top;
s->top = new_node;
s->nelems++;
}
new_node->next = s->top;
s->top = new_node;
s->nelems++;
}
Problem 2: we cannot copy the existing data pointer
into new_node. The data structure must manage its
own copy that exists for its entire lifetime. The
provided copy may go away!
Generic stack_push
new_node->next = s->top;
s->top = new_node;
s->nelems++;
}
Solution 2: make a heap-allocated copy
of the data that the node points to.
int_stack_pop
s->top = n->next;
From previous slide:
typedef struct stack { typedef struct node
free(n); int nelems; {
s->nelems--; int struct node
elem_size_bytes; *next;
node *top; void *data;
return value; } stack; } node;
}
Generic stack_pop
s->top = n->next;
return value;
}
Generic stack_pop
return value;
}
Generic stack_pop
☺
stack *
emojistack
0x800
More efficient generic stack
☺
stack *
0x800 Tricky! We will be working with
emojistack
sizeof(void *) and (void **)!!
More efficient generic stack
top
stack * 0x800
emojistack
…
elem_size_bytes
0x800 nelems 2
stack *
top
0x800 …
elem_size_bytes
emojistack
0x800 nelems 3