CS/CE 218 Lecture -*- Outline -*- connection: We've seen arrays and pointers in C. The last of the structured types is structures, which are like Pascal records. Arrays are homogeneous collections (all elements have the same type), whereas structs are heterogeneous collections (elems may have different types). * structures advert: a main data structuring mechanism in any programming language. ** What you already know (Pascal translation) /* C */ { Pascal } ----------------------------------------------------------------------------- struct person type person = { record int age; age: Integer; char name[50]; name: packed array[0..49] of Char }; end; struct person me; var me: person; struct person * you; you: ^person; me.age me.age me.age = 34; me.age := 34; you = (struct person *) new(you); malloc(sizeof(struct person)); (*you).name[3] you^.name[3] you->age you^.age struct family { type family = record int size; size: Integer; struct person *dad, *mom; dad, mom: ^person; struct person members[10]; members: array[0..9] of person; }; end; struct family mine; var mine: family; *(mine.dad).age (mine.dad)^.age mine.dad->age (mine.dad)^.age struct family *royal[3]; var royal: array[0..2] of ^family; royal[1]->size royal[1]^.size *(royal[1]).size (royal[1])^.size royal[2]->dad->age royal[2]^.dad^.age royal[2]->members[3].name[2] royal[2]^.members[3].name[2] ----------------------------------------------------------------------------- Draw pictures for these. x->y is shorthand for (*x).y precedence of -> [] and . are equal and higher than * -> [] and . group left to right royal[2]->members[3].name[2] is (((((royal[2])->members)[3]).name)[2]) *royal[1].size is *((royal[1]).size) which is different from (*royal[1]).size this is main motivation for the -> notation. ** basics of structures (section 6.1) *** types and type checking A struct declaration defines a type (as above). no space allocated for struct person {...}. can think of this as a typedef for the { ...} which is like the Pascal record ... end Although it's good practice (as in Pascal) you don't need to declare the structure type /* C */ { Pascal } ----------------------------------------------------------------------------- struct var me: { record int age; age: Integer; char name[50]; name: packed array[0..49] of Char } me; end; ----------------------------------------------------------------------------- type checking for structures is by name ------------ void structural_equivalence() { int i; struct op1 { short opcode; short addr[3]; }; struct op2 { short opcode; short addr[3]; }; struct op1 v1; struct op2 v2; struct { short opcode; short addr[3]; } v0; v2.opcode = 3; for (i = 0; i < 3; i++) { v2.addr[i] = 0; } /* following are illegal! */ v1 = v2; v0 = v1; v2 = v0; } --------------- Describe the "unique type number" model this only applies within files *** scope Q: Is the following code legal double x; struct complex { int x; int y; }; struct complex mc; /* ... */ mc.x = x; ? Yes, names in a struct have to be distinct, but don't interfere with other names in the program. (p. 130) *** operations (Section 6.2) Q: what are the operations that C provides on structures? . takes struct and member name and returns the member object can assign to it v1.opcode = 3; allocation of space by definition assignment copies value of one structure to another works this way when passed to functions initialization struct op1 v1 = { 3, {0, 0, 0}}; you have to know the order of the components other order won't do... return value from functions (by copy) take address & (structs are objects) Q: Is the code struct op1 v1, v2; /* ... */ v1 == v2 legal in C? If so what does it mean? no, it's not legal. You can't compare structures (p. 129) reasons: -can't necessarily compare bits because struct may have holes in it -to support abstract data types that have their own notion of equality (highly questionable) Q: What does assignment of struct's do? copies the values (bitwise) like "move corresponding" in COBOL *** passing structures (pages 130-131) Q: Can you pass a struct by value to a function? Yes, but for a large structure this may be inefficient Q: Can you return a struct from a function Yes again. ------------------ struct pointStruct { int x; int y;}; typedef struct pointStruct point; point makepoint(int x, int y) { point temp; temp.x = x; temp.y = y; return temp; } point reflect(point p) { p.x = - p.x; p.y = -p.y; return p; } ------------------ try point p1; p1 = makepoint(3,4); draw the picture! Notes: the typedef could also be written typedef struct { int x; int y} point; An initializer { x, y} is only understood in context, so can't write point makepoint(int x, int y) { point temp = { x, y }; return temp; } because initializer must have only constant expressions for a struct. the following is also illegal (syntax error) point makepoint(int x, int y) { return { x, y}; } In reflect, the argument is not modified, only a copy is modified (this is pass by value, not value-result or reference) Q: How would you use the result of point p = makepoint(3,4); and reflect to assign the point (-3, -4) to point x; and would that change p? no Q: Can you pass a pointer to a structure to a function? Yes, Why? because it may be more efficient, and to mutate the argument Q: How would you write reflect so it modifies its argument point? void reflect2(point * p) { (*p).x = - (*p).x; p->y = - p->y; } in (*p).x, you must use parentheses, because *p.x means *(p.x), which would try to dereference the x component Q: What is the precedence of . ->, (), and [], relative to * ? higher, they are the highest... Q: Can you complete the following table: expression fully parenthesized equivalent **p.x *(*(p.x)) *(*q)->z.y *r->a[3]->f(x) **g[2]().w->v++ ? ** example, vectors *** what is the problem? To keep track of the scores a student has, as read in by the grading program. Want to be able to read n scores, where n is determined at run-time Want to be able to sum up the scores in a list. So essentially want a list of scores. *** can you give an example? ------------ scores_read(3, &my_scores); ------------ should read 3 scores into my_scores ------------ scores_read(0, &my_scores); ------------ should leave my_scores alone ------------ scores_read(-1, &my_scores); /* ERROR */ ------------ ------------ total = scores_sum(my_scores); ----------- *** What are the givens? scores should be of type double (to fit in with rest of program) *** Develop a plan Could implement this with an array, but then don't know how many scores are in the list for sum Could keep last element as a special value, but won't pursue this idea any more (like strings in C) So will keep count of elements, store in array indexes 0 .. count-1 -------------- #define VEC_MAX_SIZE 50 struct vector { int length; double elems[VEC_MAX_SIZE]; }; typedef struct vector scores_list; -------------- Algorithm for scores_read: loop, getting each score from std_in have precondition so assume will never exceed VEC_MAX_SIZE Algorithm for scores_sum: initialize total to 0.0, add in first count elements. *** Check the plan Q: Is the plan correct? How can you tell? Algorithms are simple and well known. Do all the examples work? No, have to add precondition to scores_read: size is positive *** the file scores_list.c ---------------------- #include #include #include "print_errors.h" #include "grading.h" double scores_sum(const scores_list *v) /* ensures: result is the sum of the elements of *v */ { double sum = 0.0; int i; for (i=0; i < v->length; i++) { sum += v->elems[i]; } return sum; } void scores_read(const int num, scores_list *v) /* requires: stdin is open, num > 0, num < VEC_MAX_SIZE */ /* modifies: stdin */ /* effect: return the list of num scores read from stdin. */ { int i; for (i = 1; i <= num; i++) { if (scanf("%le", &(v->elems[i-1]))<1) { error("expecting a number"); v->elems[i-1] = 0.0; } } v->length = num; } -------------------- Note the pointers, the type double matches "%le" in scanf v->elems[i-1] is parenthesized (v->elems)[i-1] *** use in the main program (grade_estimate.c) -------------------- #include #include "grading.h" int main(int argc, char *argv[]) { scores_list possible[KINDS]; scores_list actual[KINDS]; double total[KINDS]; mins_array mins[KINDS]; FILE *minsf; int i; /* ... argument processing ...*/ minsf_get_nums(possible, minsf); for (i = 0; i < KINDS; i++) { scores_read(possible[i].length, &(actual[i])); } minsf_get_mins(possible, mins, actual, minsf); for (i = 0; i < KINDS; i++) { total[i] = scores_sum(&(actual[i])); } /* ... */ } -------------------- ** self-referential structures (section 6.5) A struct can contain a pointer to itself or another struct of the same type, call these self-referential (or recursive) types Q: Is the following struct legal struct paradox { struct paradox x; }; ? No, how would you allocate space for it? But you can write: struct paradox2 { struct paradox2 * ptr; }; struct paradox2 x; x.ptr = &x; -------------------- #include #include struct linked_list { struct graph * node; struct linked_list * next; }; typedef struct linked_list * list; list cons(struct graph *g, list n) { list temp; temp = (list) malloc(sizeof(struct linked_list)); temp->node = g; temp->next = n; return temp; } struct graph { int data; list neighbors; }; main() { struct graph g1, g2, g3; g1.data = 1; g1.neighbors = NULL; g2.data = 2; g2.neighbors = cons(&g1, NULL); g3.data = 3; g3.neighbors = cons(&g2, g2.neighbors); } -------------------- Note mutual recursion. struct graph * doesn't have to be defined before used Importance of NULL in starting things off.