/* ** $Id: control.c 449 2007-02-02 00:43:28Z phf $ ** ** Here's a mess of code we used in lecture to look at the ** basic bits and pieces of C programs. I simply added some ** comments, but you should really read the book instead of ** using this as your primary source about C programming. */ #include #include #include bool needed_below(void) { puts( "Hello from Below!" ); return true; } int main(void) { int x = 10; bool cond = (x == 10); // Basic if instruction and default format for laying // these things out. if (x < 12) { puts( "Woohoo!" ); } else { puts( "I am so sad!" ); } // Since C treats an int == 0 as false and an int != 0 // as true, you can write the following implicit comparison // (to 0). Don't do it, always put a proper condition, the // code is harder to read if you don't (as I hope you see). if (x) { puts( "Do we print this?" ); } else { puts( "Or maybe do we print this?" ); } // Always put the braces, it's too easy to get fooled by // indentation otherwise; the following prints something, // but it "looks like" it shouldn't print anything! if (x != 10) puts( "First" ); puts( "Second" ); // The sole exception to "always put braces" is for if // instructions with multiple branches; if you're doing // a bunch of "mutually exclusive" tests, and if you // put all the braces, things would be harder to read. // Just compare the following two to see what I mean: if (x == 20) { puts( "Bier" ); } else if (x == 10) { // notice the missing braces after else! puts( "Brez'n" ); } else { puts( "Hend'l" ); } if (x == 20) { puts( "Bier" ); } else { // now we put them in, but that makes everything "uneven"! if (x == 10) { puts( "Brez'n" ); } else { puts( "Hend'l" ); } } // And since we're talking about "exceptions to the rule" there // is also one regarding "implicit comparisons"; if you have a // boolean value already, such as "cond", then it is almost // *offensive* to compare that against "true" or "false" again. // It's boolean already, it doesn't become "more boolean" if // you add another test. I call it "Peter's Pet Peeve" and get // mad when I see it. Well, not really. I get sad... :-/ if (cond) { // good, cond is already boolean puts( "A" ); } else { puts( "B" ); } if (cond == true) { // bad, don't ever do this puts( "A" ); } else { puts( "b" ); } // Remember that boolean expressions get evaluated in // a "short circuit" fashion, they "stop" once the // final outcome is crystal clear. In lecture I had // a hard time coming up with an example, but here // is one now. It involves a function, but that is // not very scary; it's not about the function, it's // about which parts of an expression actually "run". bool mess = needed_below(); // runs the function bool bess = true && needed_below(); // runs it as well bool tess = true || needed_below(); // doesn't run it!!! // Checking whether a value falls within a certain // range happens quite often, but you can't use this // to get the desired result (it will print "Whooo!"): int p = 100; if (10 <= p <= 20) { // at least you get a warning here... puts( "Whooo!" ); } else { puts( "Booo!" ); } // This will work as expected though, and if you are // checking ranges, please follow this style and put // the variable you're checking "in the middle" so it // looks more like an "interval" in math does: if (10 <= p && p <= 20) { puts( "Whooo!" ); } else { puts( "Booo!" ); } // Enough with if, let's switch to switch for a very, // very short time. I forget to put the breaks, so I // never use this. I also find the syntax quite ugly // in general. But if something needs to be really, // really fast: an equivalent sequence of nested if // instructions is slower than a switch. Most of the // time that doesn't matter however... switch(p) { case 100: puts( "It's a 100" ); // break; // if you forget the break, you "fall through"! case 20: puts( "It's a 20" ); break; default: puts( "It's something" ); break; // sorta optional here, but put it anyway! } // Alright, now let's look briefly at the various kinds // of loops; given all the discussion above, we can keep // this really short now. :-) int i = 0; // First the plain old while loop; we test the condition // before we enter the body, so if it is false initially, // the body *never* runs. while (i < 4) { printf( "While %d\n", i ); i += 1; // I prefer this to i++ unless it's part of a for loop } // Same loop as above now using for, collects // initialization, condition, and modification // for the loop in one place, often a good idea. for (i = 0; i < 4; i++) { printf( "For %d\n", i ); } // Here's an example for the "continue" instruction which // "skips ahead" to the next iteration. I don't like using // continue or break, I think loops should have their // conditions on the "outside" not on the "inside"; easier // to read and reason about that way. for (i = 0; i < 10; i++) { if (i % 2 == 0) continue; // sad printf( "Odd %d\n", i ); } // The above loops check the condition before // entering the loop body, this one always // enters the body at least once; sometimes // useful. So how often does this loop run? :-) i = 0; do { i += 1; printf( "Do %d\n", i ); } while (i < 4); // Infinite loop with explicit breaks; usually much harder // to read, again because the conditions for which the loop // terminates are now all over the place on the "inside" // instead of clearly in one place on the "outside" of the // loop. However, sometimes loops like this are useful! If // you really need one, write it like this; "for(;;)" is // pronounced "forever" for the obvious reason. :-) i = 0; for (;;) { printf( "Forever %d\n", i ); if (i > 4) { break; } i += 1; } // Finally, since you're all grown-ups... Let's build a // loop out of goto instructions. But don't ever, EVER // do this, there is *no* redeeming quality here, none, // zippo. i = 0; loop: if (i > 4) goto done; printf( "Argh %d\n", i ); i += 1; goto loop; done: puts( "Whew..." ); return EXIT_SUCCESS; } /* ** Really, read the book, this file is not enough to get ** these portions of C! */