Week 11 - Chapter 10 Program Organization Review Chapter 9 Homework ------------------------- Local variables --------------- We've already seen that there are several kinds of variables, both in terms of type and or number. But the kinds of variables we have access to in C will grow once more. The first kind for us to talk about is the local variable. We start with the local variable because it is the kind of variable that we have been using all along. Local variables are declared at the top of a function, and may only be used inside that function. Any attempt to use the variable in another function will cause a compiler error. main() { int i; ... return(0); } void fn(void) { i = 2; // We are trying to set main's i, but cannot return; } The concept of the local variable requires us to know about a major concept in C, and other languages. The concept is "scope." Scope ----- What scope basically refers to is where a variable can be seen. The scope of any variable is the block in which it is declared, and all sub-blocks. Remember that a block is any series of statements found between curly braces, including a function. A variable declared in a block is said to have scope within the block. void fn(void) { <---- start of block int i; for (i = 0; i<10; i++) { <---- start of another block printf("%d\n",i); <---- i is accessible in the sub-block } return; } I am implying something when I say that a variable's scope is the block in which it is declared. Because of what a block is, statements found between a pair of curly braces, does that mean I can do this: void fn(void) { int i; for (i=0; i<10; i++) { int j = 9; printf("%d %d\n",i,j); } return; } As a matter of fact, we can do this. j is local to the block in which it is declared, the for loop only. If we add: printf("%d\n",j); after the for loop, the compiler will stop us. In a function, when we declare a variable like this (int j = 9), each time we run the function, we get a new version of j, freshly initialized to 9 - so how does it work in the for loop? The j is created and gets the value of 9 every time the for loop begins. If the for loop changes the 9 to something else within the loop, that new value is then retained until the loop restarts. for (i=0; i<10; i++) { int j = 9; <--- create j, set to 9 printf("%d %d\n",i ,j); <--- print j (9) j++; <--- bump up j to 10 } This prints "0 9", "1 9", and so on. Every time the loop restarts, it recreates the j with a fresh value of 9. Just like a function. The reason? One moment... Note that we cannot place the "int j" in the initializer of the for loop, as that is outside the block, and we cannot increase j by 1 in the change, because the change is also outside the block. Auto ---- The reason that the j reverts to 9 each time through the loop is that it is an automatic variable. All variables are automatic by default. If you want to be explicit, you can use the auto keyword: for (i=0; i<10; i++) { auto int j = 9; printf("%d %d\n",i ,j); j++; } However, no one ever uses the auto keyword. If there is an auto keyword, then there must be something that opposes "auto," and there is. Static. Static ------ The static keyword gives the variable a special status. What happens with an automatic variable is that every time the program sees a reference to the declaration, it goes out and grabs a piece of memory for the variable. When the block ends, the memory for the variable is freed. Each time in the loop, it is possible that we will get a new memory location. The static keyword tells the program to do something special: to assign it a memory location that never changes. After the memory has been assigned the first time, that variable will always have the piece of memory. Moreover, the data stored in the memory location will be retained: for (i=0; i<10; i++) { static int j = 9; printf("%d %d\n", i, j); j++; } Now we get "0 9", "1 10", "2 11", and so on. This time, j is assigned memory once, and the memory is retained for the life of the program. What about the initialization value? With a static variable, the initialization value is used only when the memory is assigned, and never again. We are saying here, "create memory for j, and set the memory to 9". Each subsequent time, we say, "give me access to whatever is in j". The program keeps track of the first time a variable is seen so we don't have to. Note that because static causes the value to only be initialized once, it will continue to grow each time this for loop runs. Functions --------- Automatic and static variables are, of course, available to functions as well as to any block. It is here that they are most handy. Let's look at an example of a function that retains a counter value, and changes it and returns it when the function is called. Our first crack might look like this: int count(void) { int counter = 0; counter++; return(counter); } When we run this, we will soon find that count always returns a 1 each time it is called, no matter how many times it is called. This is because counter is automatic. To fix this, we just need to make counter be static: int count(void) { static int counter = 0; counter++; return(counter); } Now count will return 1, then 2, then 3, and so on. As a side note, the parameters to a function are considered local automatic variables to the function - they cannot be made static. Global variables ---------------- Since we call them local variables, it seems to follow that there is something to oppose local ... and of course there is. The global variable. The main differences between a local variables and global variables are these: 1. locals are defined within a block globals are defined outside of any block 2. locals have scope of the block in which they are declared globals have scope of the entire file, from the point they are declared Some languages include support for only global variables. In C, generally, we avoid globals. They are not taboo, but most things can be done with locals. Globals, however, can come in quite handy. Two examples: int score; void print_score(void) { printf("Your score is: %d\n",score); return; } void increase_score(int amount) { score += amount; return; } main() { score = 0; ... increase_score(10); print_score(); ... return(0); } Here, the score variable is declared outside of any block, so it is global. All functions in the entire program have access to the data. This might make our program slightly faster, since the alternative is to have many local automatic variables that get created and destroyed on demand. It can reduce clutter, because the alternative is to declare pass the score variable back and forth to the functions: void print_score(int score) { printf("Your score is: %d\n",score); return; } int increase_score(int amount, int score) { score += amount; return(score); } main() { int score = 0; ... score = increase_score(10,score); print_score(score); ... return(0); } This code is perfectly acceptable, and some programmers would prefer it. But others will find the first example easier to understand. The second main example is if there is a large block of data that will be shared by many functions in the program. For example, there may be a 200 or 300 byte settings record that needs to be read from disk, written to disk, examined, set, reset, etc. Having one global block of data can make these operations much easier. One more note: the text refers to these variables as "external variables," which I may use from time to time. However, there is a variable modifier called "extern" that we won't be delving into, and I tend to use global to avoid confusion between "extern" variable and global variables. Blocks ------ We've seen blocks used in functions, loops, and conditionals, as well as the switch statement. Turns out, we can create blocks out of nothing. There is usually little reason for doing so, but it is possible: void fn(void) { int i; i = 0; { int j; j = 2; } return; } Here, a block is created solely to create a short-lived automatic variable. Again, not much usefulness, but it is perfectly legal. This is called an anonymous block. Scope resolution ---------------- In several examples, I have used the same variable name in several functions. This is not a problem because the scope of the variables has been local, so they do not conflict: void fn1(void) { int i; ... } void fn2(void) { int i; ... } The two "i's" are completely different variables. Even if they are made static, they will not conflict. But what about this: int score = 100; void init_score(void) { int score; ... score = 0; ... } Here we have a global, which every function can see, and a local, which only the init_score function can see ... within init_score, how do we know which score variable is being set to 0? Easy: in C, when there are two variables with different levels of scope, the most recently defined one wins. So here, the score that is declared in the init_score function is the one that gets set to 0. The global score is left alone. In fact, there is no way, at all, for the init_score function to change the value of the global score. Program organization -------------------- Pick a general format and stick to it. Here is mine: #defines that the #includes require, and maybe all #defines #includes possibly more #defines global variables declarations function prototypes main function declarations The text recommends the same basic pattern, but adds type declarations (with typedef), which I tend to not use. Other than that, this order is pretty universal, if for no other reason than it is logical. Homework -------- The homework is going to consist of changing the poker.c program found on page 199. I do suggest that you type this in yourself. There is real value in transcribing a program you know works, and tracking down the errors in your transcription with the compiler. If you wish to download the code, however, you can find a copy on the web site.