/* ** $Id: structs.c 789 2008-02-23 06:27:49Z phf $ ** ** This starts with lots of declarations and definitions, ** code that uses them follows. Gotta read the whole mess ** to get the full picture, sorry. :-/ */ #include #include #include // Structures allow us to give *one* name to *several* // variables, potentially of different types: struct person // structure tag (optional) { char *name; int age; char gender; } paul, mary; // variables of that type (optional) // Here's another example we'll use again below (could // have used it above for "birthdate" as well I guess). struct date { int year; int month; int day; } today, tomorrow; // So these things are a little like classes in Java, // but they only hold data, not code. Once we have a // structure defined, we can also make variables like // this: struct person peter; struct date whenever; // Note that the "structure tag" itself is not a type // name, so we can't say "date whenever;" to declare // a variable; we always need to say "struct date" // instead. That can be painful (not too painful, but // anyway) so here's a new mechanism that allows us // to *not* write "struct bla" everywhere: typedef struct date Date; typedef struct person Person; // Using "typedef" we can introduce a new *type* name // to stand for an existing type. So here we're saying // "Whenever I use 'Person' in the future, I really // mean 'struct person' but don't want to write it." Date important; Person very_important; // The "typedef" mechanism works for any kind of type: typedef int whole_number; // This simply introduces a new name for the existing // "int" type. Compare: int a_first_int = 10; whole_number another_int = 20; // Except for getting a new type name out of it, there's // nothing mysterious about "typedef". If we don't care // about the (optional) structure tag, we can declare a // type usable in further definitions by combining a // typedef with a struct; you'll see this a lot in some // kinds of code, so you should be familiar with it: typedef struct { int one; double two; } Something; // Note that here "something" is *not* a variable, it's // the name we're defining with "typedef" in the first // place! // We can initialize struct variables similar to the // way we could arrays: Date arrival = {2007, 2, 18}; struct date departure = {2007, 2, 20}; // We can also use structs within structs, building a // hierarchy of abstractions. If you're reminded of XML // here, that's not an accident. Check this out: struct event { struct date start; struct date end; char name[80]; }; // We can even initialize variables of those compound // structs by nesting initializers for the individual // components: struct event march = { {2007, 3, 17}, {2007, 3, 17}, "March on the Pentagon!" }; // Here are a few functions for printing things like // date, event, and person; see main() for how they // are used. void print_date( struct date d ) { // We pass the date directly and it gets *copied* to a local // variable named "d" which only exists inside the function. printf( "%d/%d/%d\n", d.year, d.month, d.day ); // If we assigned to "d", it won't affect the struct we passed // in, it's "call-by-value" again, unlike for arrays where we // always pass a pointer to the first element. d.year = 1907; } void print_person( const Person *p ) { // If we want to avoid the copy, we must pass a pointer // to the struct. If we then want to avoid changing it // but accident, we make it const. Of course we have to // dereference "p" now before accessing a field: printf( "Name: %s\n", (*p).name ); // To avoid the awkward parentheses (which are needed here!) // C introduces an abbreviation that's easier on the eyes: printf( "Age: %d\n", p->age ); // It means the exact same thing! Let's print the gender // twice, once each way: printf( "Gender: %c %c\n", p->gender, (*p).gender ); } void print_event( const struct event e ) { // print the name printf( "%s\n", e.name ); // print the dates using our existing functions print_date( e.start ); print_date( e.end ); } // If we want a function to change the struct we pass, // we need to use pointers again. If we do not, all we // do is modify a local copy of the struct, which will // be lost once we return... void age_person( Person *p ) { p->age += 1; } // Here's the main... :-) int main( void ) { // Let's play with dates first, using "." to access fields. today.year = 2007; today.month = 2; today.day = 13; print_date( today ); // pass by value puts( "----------" ); // Next let's make a person. paul.name = "Paul"; paul.age = 32; paul.gender = 'M'; print_person( &paul ); // pass by reference (pointer!) puts( "----------" ); // We can assign structs to each other, unlike arrays! // So this should make you freak out... If not, see // below for the array code... peter = paul; peter.name = "Peter"; print_person( &peter ); puts( "----------" ); // Here's the same thing with arrays, comment it out // to check but it won't work. Ask Brian Kernighan... // char a[20], b[20]; // a = b; // Of course we can try to be tricky, no further comment // on the crazyness of it all... :-) struct { char something[20]; } r, s; // two variables of tagless struct, in case you forgot strcpy(s.something, "Peter here!"); strcpy(r.something, "Nobody in!"); puts(s.something); puts(r.something); r = s; puts(s.something); puts(r.something); // wow puts( "----------" ); // Print a compound struct. print_event( march ); puts( "----------" ); // Accessing a compound field. printf( "%d\n", march.end.year ); puts( "----------" ); // Make Paul a year older and print again. age_person( &paul ); print_person( &paul ); puts( "----------" ); // We can define recursive structs, but we need // to use pointers to do so. We'll see more of // this when we talk about linked lists. struct recursive { struct recursive *next; }; // One last thing: Each "struct" generates a new // type, not compatible with any other struct. So // the following two types are *not* compatible! typedef struct { int x; int y; } Point; typedef struct { int x; int y; } Doint; Point p = {10, 20}; Doint d = {20, 30}; // p = d; // doesn't work, try it! p.x = d.x; // works fine since they are both ints p.y = d.y; // ditto // A total tangent, not related to structs at all but that's // where we happened to talk about it in lecture... :-) (We // actually did this earlier already in Spring 2008.) char *one = "Peter"; // write protected string char two[] = "Peter"; // writable string one[1] = 'x'; // could trigger segfault (but doesn't on OS X?) two[1] = 'x'; // fine puts( one ); // this one didn't change (if you get here) puts( two ); // this one did change (if you get here) return EXIT_SUCCESS; }