Download presentation
Presentation is loading. Please wait.
Published byDaniel Copeland Modified over 9 years ago
1
More on Abstract Data Types Noah Mendelsohn and Mark Sheldon Tufts University Email: noah@cs.tufts.edunoah@cs.tufts.edu Web: http://www.cs.tufts.edu/~noah COMP 40: Machine Structure and Assembly Language Programming (Fall 2015)
2
© 2010 Noah Mendelsohn Last time we covered How data and pointers are stored in the computer’s memory Abstraction, modularity, reuse, information hiding Generalizing over values and types The special role of void * and incomplete structures 2 Today Generalizing over computation Function pointers Iteration and mapping in a functional style Boxed and unboxed data structures
3
© 2010 Noah Mendelsohn Quick Review
4
© 2010 Noah Mendelsohn Software structures model real world objects and concepts Integers Students Bank statements Photographic images Sound recordings Etc. 4 These things aren’t bits They don’t live in computers, but…
5
© 2010 Noah Mendelsohn Software structures model real world objects and concepts Integers Students Bank statements Photographic images Sound recordings Etc. 5 We build data structures that model them The course materials refer to these as being in the “World of Ideas”
6
© 2010 Noah Mendelsohn Key principles to watch for Abstraction –Refers to the act of “pulling away from” something so that unimportant details are hidden” 1 Modularity & clean interfaces –Each module “does one thing well” 2 Information hiding –Modules keep details of their internals secret; makes them more modular Generalization –When practical, each module or service should be as generally useful as possible 6 1 Prof. Mark Sheldon – COMP 11 Big IdeasCOMP 11 Big Ideas 2 Ken Thompson, co-inventor of Unix – The Unix PhilosophyThe Unix Philosophy
7
© 2010 Noah Mendelsohn Interfaces 7 Interface Implementation Client E.g. your fgroups program E.g. implementation of Hanson Table_T E.g. methods in Hanson table.h
8
© 2010 Noah Mendelsohn Interfaces 8 Interface Implementation Client The implementation chooses a representation … NOT visible to the client
9
© 2010 Noah Mendelsohn Interfaces 9 Interface Implementation Client E.g. your fgroups program E.g. implementation of Hanson Table_T E.g. methods in Hanson table.h My_table = Table_new(… … …) Q. What can Table_new return? Last time we showed you two ways of doing this
10
© 2010 Noah Mendelsohn Client doesn’t know secret implementation 10 Interface Implementation Client my_table = Table_new(… … …) Struct Table_t { …data declartions… } struct Table_T *table_ptr; void *table_ptr;
11
© 2010 Noah Mendelsohn Client doesn’t know secret implementation 11 Interface Implementation Client my_table = Table_new(… … …) Struct Table_t { …data declartions… } struct Table_T *table_ptr; void *table_ptr; Note: This declaration would be in the publicly used table.h!
12
© 2010 Noah Mendelsohn Client doesn’t know secret implementation 12 Interface Implementation Client my_table = Table_new(… … …) Struct Table_t { …data declartions… } struct Table_T *table_ptr; void *table_ptr; Note: These declarations are in the implementation (either in a table.c, or in a tableimp.h)
13
© 2010 Noah Mendelsohn Hanson does this with incomplete structs 13 Interface Implementation Client Struct Table_t { …data declartions… } struct Table_T *table_ptr; struct Table_t *table_ptr; Client has incomplete declaration of the struct my_table = Table_new(… … …) Table_put(my_table, …)
14
© 2010 Noah Mendelsohn By the way, this is exactly what languages like C++ do 14 class Myclass { int some_method(int a) {…}; } Myclass my_object = new Myclass(); my_object -> some_method(5); Under the covers, C++ implements this as: some_method(my_object, 5);
15
© 2010 Noah Mendelsohn Generalization
16
© 2010 Noah Mendelsohn You’ve already seen some generalization 16 int square3() { return 3 * 3; } We don’t do this int square(int n) { return n * n; } We do this! Generalize over input value Can we generalize over the type of information?
17
© 2010 Noah Mendelsohn We need to generalize over types 17 List_of_students_new(…); List_of_cars_new(…); List_of_bank_stmts_new(…); We don’t want this List_new(…); We want this! How do we declare the input to List_push()? (after all, its type could be anything) Can we generalize over the type of information?
18
© 2010 Noah Mendelsohn How do we declare List_push? 18 void List_push(List_T list, void *x); Hanson’s declaration for List_push struct Car {char *brand; int weight}; typedef struct Car Car; Car mycar = {“ford”, 2000}; Car *retrieved_car; mylist = List_list(NULL); /* create empty list */ List_push(mylist, &mycar); /* put my car on the list */
19
© 2010 Noah Mendelsohn Void * allows us to generalize over types 19 void List_push(List_T list, void *x); Hanson’s declaration for List_push The list will remember a pointer to anything. struct Car {char *brand; int weight}; typedef struct Car Car; Car mycar = {“ford”, 2000}; Car *retrieved_car; mylist = List_list(NULL); /* create empty list */ List_push(mylist, &mycar); /* put my car on the list */ Any pointer can be passed to a void * parameter
20
© 2010 Noah Mendelsohn Void * allows us to generalize over types 20 void List_push(List_T list, void *x); Hanson’s declaration for List_push struct Car {char *brand; int weight}; typedef struct Car Car; Car mycar = {“ford”, 2000}; Car *retrieved_car_p; mylist = List_list(NULL); /* create empty list */ List_push(mylist, &mycar); /* put my car on the list */ List_pop(mylist, &retrieved_car_p); /* get back mycar */ This generalization is known as universal polymorphism
21
© 2010 Noah Mendelsohn Void * allows us to generalize over types 21 void List_push(List_T list, void *x); Hanson’s declaration for List_push struct Car {char *brand; int weight}; typedef struct Car Car; Car mycar = {“ford”, 2000}; Car *retrieved_car_p; mylist = List_list(NULL); /* create empty list */ List_push(mylist, &mycar); /* put my car on the list */ List_pop(mylist, &retrieved_car_p); /* get back mycar */ IMPORTANT: Retrieved_car_p is already a pointer. Why do we have to pass the address of retrieved_car_p?
22
© 2010 Noah Mendelsohn Aside: how can a function modify arguments passed to it?
23
© 2010 Noah Mendelsohn Can a function change its argument? 23 void doubleit(int n) { n = n * 2; return; } int x = 3; doubleit(x); printf(“x = %d\n”, x); This won’t work void doubleit(int *np) { *np = *np * 2; return; } int x = 3; doubleit(&x); printf(“x = %d\n”, x); This will work Prints: x = 3 Prints: x = 6
24
© 2010 Noah Mendelsohn What if the variable to be changed is already a pointer? 24 void getstring(char **stringpp) { *stringpp =...; return; } char *myString; getstring(&myString); Does this remind you of readaline ?
25
© 2010 Noah Mendelsohn Generalizing Over Computation
26
© 2010 Noah Mendelsohn Three ways of generalizing Parameters generalize values To generalize over types (of pointers) in C, we use void pointers (other languages have more direct ways of doing this) To generalize over computations, use function pointers 26
27
© 2010 Noah Mendelsohn Why generalize over function? We’ve already generalized types…sometimes we need different code to work on each type –Classic example: we’re building a sort routine that can sort anything… –…the rules for sorting different types of things are different Mapping: do the same operation on each entry in a collection Providing or overriding behavior in a common implementation –Overriding default output format –Overriding data source for some input routine –Etc. 27
28
© 2010 Noah Mendelsohn Function pointers in C We know the code is living in memory Can we take the address of code? Yes! 28 Syntax int square(int n) { return n * n; } int (*somefunc)(int n); somefunc = □ printf(“3 squared is %d\n”, (*somefunc)(3));
29
© 2010 Noah Mendelsohn Function pointers in C We know the code is living in memory Can we take the address of code? Yes! 29 Syntax int square(int n) { return n * n; } int (*somefunc)(int n); somefunc = □ printf(“3 squared is %d\n”, (*somefunc)(3));
30
© 2010 Noah Mendelsohn Function pointers in C We know the code is living in memory Can we take the address of code? Yes! 30 Syntax int square(int n) { return n * n; } int (*somefunc)(int n); somefunc = □ printf(“3 squared is %d\n”, (*somefunc)(3));
31
© 2010 Noah Mendelsohn Function pointers in C We know the code is living in memory Can we take the address of code? Yes! 31 Syntax int square(int n) { return n * n; } int (*somefunc)(int n); somefunc = □ printf(“3 squared is %d\n”, (*somefunc)(3));
32
© 2010 Noah Mendelsohn Be sure you understand why this works! 32 int square(int n) { return n * n ; } int cube(int n) { return n * n * n ; } int main(int argc, char *argv[]) { int (*somefunc)(int n); somefunc = □ printf("3 squared is %d\n", (*somefunc)(3)); somefunc = &cube; printf("3 cubed is %d\n", (*somefunc)(3)); return EXIT_SUCCESS; }
33
© 2010 Noah Mendelsohn Be sure you understand why this works! 33 int square(int n) { return n * n; } int cube(int n) { return n * n * n; } int main(int argc, char *argv[]) { int (*somefunc)(int n); somefunc = □ printf("3 squared is %d\n", (somefunc)(3)); somefunc = &cube; printf("3 cubed is %d\n", (*somefunc)(3)); return EXIT_SUCCESS; } The “*” is optional… …modern versions of C provide it for you if you leave it out.
34
© 2010 Noah Mendelsohn Using function pointers You’ve seen a simple example We often pass function pointers as arguments to other functions This allows us to generalize over functionals/computations Examples from Hanson: –cmp and hash routines for tables –apply() functions for mapping 34
35
© 2010 Noah Mendelsohn Computing on Collections
36
© 2010 Noah Mendelsohn One option: loops 36 /* NOT how Hanson usually does it */ tab = Table_new(…): …add some data here.. for (i = 0; i < Table_length(tab); ++i) { upcase(Table_getnext(tab, NEXT)); } This is pseudo-code: some details just to show the idea. Hanson doesn’t do it this way anyway.
37
© 2010 Noah Mendelsohn The functional (Hanson) way 37 void upcasefunction(void *key, void **value, void *cl) { …can uppercase **value here.. } Tab = Table_new(…); …add some data here… Table_map(tab, upcasefunction, ??);
38
© 2010 Noah Mendelsohn The functional (Hanson) way 38 void upcasefunction(void *key, void **value, void *cl) { …can uppercase **value here.. } tab= Table_new(…): …add some data here.. Table_map(tab, upcasefunction, ??); The map Table_map function loops calling upcasefunction repeatedly, once for each entry Passing pointer to function
39
© 2010 Noah Mendelsohn The closure: shared data for the mapping 39 void upcasefunction(void *key, void **value, void *cl) { …can uppercase **value here.. } struct cl mycl {…shared data here..}; tab= Table_new(…): …add some data here.. Table_map(tab, upcasefunction, &mycl); The data in mycl is passed in from the caller… …can be seen and updated in upcasefunction
40
© 2010 Noah Mendelsohn Mapping vs loops Map advantages: –Clean, easy to reason about –Often less code to write –Automatically gets the iteration right (e.g. length) Disadvantages –Less flexible: what if we want to iterate in unusual order? –Less obvious how to iterate multiple structures at once (there are ways) –Getting closures right can be tricky Mapping is a classic functional programming construct 40
41
© 2010 Noah Mendelsohn Unboxed Data Generalizing to Collections of Objects that Aren’t Pointers
42
© 2010 Noah Mendelsohn Boxed and unboxed collections The collections you’ve seen so far are boxed List_push(mylist, &data); /* list stores a pointer */ Problem: we want an array of 1 million characters –A character is 1 byte, but a pointer to a character is 8 bytes –Even for bigger types, chasing pointers takes time, and allocating memory can be expensive –Sometimes, we get arrays from or provide arrays to other code Unboxed collections: store the actual data, not a pointer! /* array of 1000000 chars */ my_array = UArray_new(1000000, sizeof(char)); /* set the char at offset 500 to ‘X’ */ char *mychar = UArray_at(my_array, 500); *mychar = ‘X’; 42
43
© 2010 Noah Mendelsohn Boxed and unboxed collections The collections you’ve seen so far are boxed List_push(mylist, &data); /* list stores a pointer */ Problem: we want an array of 1 million characters –A character is 1 byte –A pointer to a character is 8 bytes –Even for bigger types, chasing pointers takes time, and allocating memory can be expensive Unboxed collections: store the actual data, not a pointer! /* array of 1000000 chars */ my_array = UArray_new(1000000, sizeof(char)); /* set the char at offset 500 to ‘X’ */ char *mychar = UArray_at(my_array, 500); *mychar = ‘X’; 43 Space for 1000000 characters is allocated in the Hanson uarray…we could use sizeof(struct SomeBigStruct) and that would work too!
44
© 2010 Noah Mendelsohn Boxed and unboxed collections The collections you’ve seen so far are boxed List_push(mylist, &data); /* list stores a pointer */ Problem: we want an array of 1 million characters –A character is 1 byte –A pointer to a character is 8 bytes –Even for bigger types, chasing pointers takes time, and allocating memory can be expensive Unboxed collections: store the actual data, not a pointer! /* array of 1000000 chars */ my_array = UArray_new(1000000, sizeof(char)); /* set the char at offset 500 to ‘X’ */ char *mychar = UArray_at(my_array, 500); *mychar = ‘X’; 44 Uarray_at returns a pointer directly into the data array that’s inside the Hanson implementation…that’s its contract.
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.