Warning: Declaration of action_plugin_subjectindex_indexer::register(&$controller) should be compatible with DokuWiki_Action_Plugin::register(Doku_Event_Handler $controller) in /data/web/virtuals/28604/virtual/www/subdom/bo/lib/plugins/subjectindex/action/indexer.php on line 15

Warning: Declaration of action_plugin_mathjax_enable::register(Doku_Event_Handler &$controller) should be compatible with DokuWiki_Action_Plugin::register(Doku_Event_Handler $controller) in /data/web/virtuals/28604/virtual/www/subdom/bo/lib/plugins/mathjax/action/enable.php on line 62

Warning: Declaration of action_plugin_googleanalytics::register(&$controller) should be compatible with DokuWiki_Action_Plugin::register(Doku_Event_Handler $controller) in /data/web/virtuals/28604/virtual/www/subdom/bo/lib/plugins/googleanalytics/action.php on line 40

Warning: Declaration of action_plugin_folded::register(&$controller) should be compatible with DokuWiki_Action_Plugin::register(Doku_Event_Handler $controller) in /data/web/virtuals/28604/virtual/www/subdom/bo/lib/plugins/folded/action.php on line 40

Warning: Declaration of action_plugin_hidden::register(&$controller) should be compatible with DokuWiki_Action_Plugin::register(Doku_Event_Handler $controller) in /data/web/virtuals/28604/virtual/www/subdom/bo/lib/plugins/hidden/action.php on line 28

Warning: Declaration of action_plugin_include::register(&$controller) should be compatible with DokuWiki_Action_Plugin::register(Doku_Event_Handler $controller) in /data/web/virtuals/28604/virtual/www/subdom/bo/lib/plugins/include/action.php on line 354

Warning: Declaration of action_plugin_tag::register(&$contr) should be compatible with DokuWiki_Action_Plugin::register(Doku_Event_Handler $controller) in /data/web/virtuals/28604/virtual/www/subdom/bo/lib/plugins/tag/action.php on line 175

Warning: Cannot modify header information - headers already sent by (output started at /data/web/virtuals/28604/virtual/www/subdom/bo/lib/plugins/subjectindex/action/indexer.php:15) in /data/web/virtuals/28604/virtual/www/subdom/bo/inc/auth.php on line 532

Warning: preg_replace(): The /e modifier is no longer supported, use preg_replace_callback instead in /data/web/virtuals/28604/virtual/www/subdom/bo/inc/auth.php on line 818

Warning: Cannot modify header information - headers already sent by (output started at /data/web/virtuals/28604/virtual/www/subdom/bo/lib/plugins/subjectindex/action/indexer.php:15) in /data/web/virtuals/28604/virtual/www/subdom/bo/inc/actions.php on line 656

Warning: Cannot modify header information - headers already sent by (output started at /data/web/virtuals/28604/virtual/www/subdom/bo/lib/plugins/subjectindex/action/indexer.php:15) in /data/web/virtuals/28604/virtual/www/subdom/bo/inc/actions.php on line 656
lcthw:ex22

======Exercise 22: The Stack, Scope, And Globals====== The concept of "scope" seems to confuse quite a few people when they first start programming. Originally it came from the use of the system stack (which we lightly covered earlier) and how it was used to store temporary variables. In this exercise, we'll learn about scope by learning about how a stack data structure works, and then feeding that concept back in to how modern C does scoping. The real purpose of this exercise though is to learn where the hell things live in C. When someone doesn't grasp the concept of scope, it's almost always a failure in understanding where variables are created, exist, and die. Once you know where things are, the concept of scope becomes easier. This exercise will require three files: ex22.h A header file that sets up some external variables and some functions. ex22.c Not your main like normal, but instead a source file that will become a object file ex22.o which will have some functions and variables in it defined from ex22.h. ex22_main.c The actual main that will include the other two and demonstrate what they contain as well as other scope concepts. ex22.h and ex22.c Your first step is to create your own header file named ex22.h which defines the functions and "extern" variables you need: #ifndef _ex22_h #define _ex22_h // makes THE_SIZE in ex22.c available to other .c files extern int THE_SIZE; // gets and sets an internal static variable in ex22.c int get_age(); void set_age(int age); // updates a static variable that's inside update_ratio double update_ratio(double ratio); void print_size(); #endif The important thing to see is the use of extern int THE_SIZE, which I'll explain after you also create the matching ex22.c: #include <stdio.h> #include "ex22.h" #include "dbg.h" int THE_SIZE = 1000; static int THE_AGE = 37; int get_age() { return THE_AGE; } void set_age(int age) { THE_AGE = age; } double update_ratio(double new_ratio) { static double ratio = 1.0; double old_ratio = ratio; ratio = new_ratio; return old_ratio; } void print_size() { log_info("I think size is: %d", THE_SIZE); } These two files introduce some new kinds of storage for variables: extern This keyword is a way to tell the compiler "the variable exists, but it's in another 'external' location". Typically this means that one .c file is going to use a variable that's been defined in another .c file. In this case, we're saying ex22.c has a variable THE_SIZE that will be accessed from ex22_main.c. static (file) This keyword is kind of the inverse of extern and says that the variable is only used in this .c file, and should not be available to other parts of the program. Keep in mind that static at the file level (as with THE_AGE here) is different than in other places. static (function) If you declare a variable in a function static, then that variable acts like a static defined in the file, but it's only accessible from that function. It's a way of creating constant state for a function, but in reality it's rarely used in modern C programming because they are hard to use with threads. In these two files then, you have the following variables and functions that you should understand: THE_SIZE This is the variable you declared extern that you'll play with from ex22_main.c. get_age and set_age These are taking the static variable THE_AGE, but exposing it to other parts of the program through functions. You couldn't access THE_AGE directly, but these functions can. update_ratio This takes a new ratio value, and returns the old one. It uses a function level static variable ratio to keep track of what the ratio currently is. print_size Prints out what ex22.c thinks THE_SIZE is currently. ex22_main.c Once you have that file written, you can then make the main function which uses all of these and demonstrates some more scope conventions: #include "ex22.h" #include "dbg.h" const char *MY_NAME = "Zed A. Shaw"; void scope_demo(int count) { log_info("count is: %d", count); if(count > 10) { int count = 100; // BAD! BUGS! log_info("count in this scope is %d", count); } log_info("count is at exit: %d", count); count = 3000; log_info("count after assign: %d", count); } int main(int argc, char *argv[]) { // test out THE_AGE accessors log_info("My name: %s, age: %d", MY_NAME, get_age()); set_age(100); log_info("My age is now: %d", get_age()); // test out THE_SIZE extern log_info("THE_SIZE is: %d", THE_SIZE); print_size(); THE_SIZE = 9; log_info("THE SIZE is now: %d", THE_SIZE); print_size(); // test the ratio function static log_info("Ratio at first: %f", update_ratio(2.0)); log_info("Ratio again: %f", update_ratio(10.0)); log_info("Ratio once more: %f", update_ratio(300.0)); // test the scope demo int count = 4; scope_demo(count); scope_demo(count * 20); log_info("count after calling scope_demo: %d", count); return 0; } I'll break this file down line-by-line, and as I do you should find each variable I mention and where it lives. ex22_main.c:4 Making a const which stands for constant and is an alternative to using a define to create a constant variable. ex22_main.c:6 A simple function that demonstrates more scope issues in a function. ex22_main.c:8 Prints out the value of count as it is at the top of the function. ex22_main.c:10 An if-statement that starts a new scope block, and then has another count variable in it. This version of count is actually a whole new variable. It's kind of like the if-statement started a new "mini function". ex22_main.c:11 The count that is local to this block is actually different from the one in the function's parameter list. What what happens as we continue. ex22_main.c:13 Prints it out so you can see it's actually 100 here, not what was passed to scope_demo. ex22_main.c:16 Now for the freaky part. You have count in two places: the parameters to this function, and in the if-statement. The if-statement created a new block, so the count on line 11 does not impact the parameter with the same name. This line prints it out and you'll see that it prints the value of the parameter, not 100. ex22_main.c:18-20 Then I set the parameter count to 3000 and print that out, which will demonstrate that you can change function parameters and they don't impact the caller's version of the variable. Make sure you trace through this function, but don't think that you understand scope quite yet. Just start to realize that if you make a variable inside a block (as in if-statements or while-loops), then those variables are new variables that exist only in that block. This is crucial to understand, and is also a source of many bugs. We'll address why you shouldn't do this shortly. The rest of the ex22_main.c then demonstrates all of these by manipulating and printing them out: ex22_main.c:26 Prints out the current values of MY_NAME and gets THE_AGE from ex22.c using the accessor function get_age. ex22_main.c:27-30 Uses set_age in ex22.c to change THE_AGE and then print it out. ex22_main.c:33-39 Then I do the same thing to THE_SIZE from ex22.c, but this time I'm accessing it directly, and also demonstrating that it's actually changing in that file by printing it here and with print_size. ex22_main.c:42-44 Show how the static variable ratio inside update_ratio is maintained between function calls. ex22_main.c:46-51 Finally running scope_demo a few times so you can see the scope in action. Big thing to notice is that the local count variable remains unchanged. You must get that passing in a variable like this will not let you change it in the function. To do that you need our old friend the pointer. If you were to pass a pointer to this count, then the called function has the address of it and can change it. That explains what's going on in all of these files, but you should trace through them and make sure you know where everything is as you study it. ======What You Should See====== This time, instead of using your Makefile I want you to build these two files manually so you can see how they are actually put together by the compiler. Here's what you should do and what you should see for output. $ cc -Wall -g -DNDEBUG -c -o ex22.o ex22.c $ cc -Wall -g -DNDEBUG ex22_main.c ex22.o -o ex22_main $ ./ex22_main [INFO] (ex22_main.c:26) My name: Zed A. Shaw, age: 37 [INFO] (ex22_main.c:30) My age is now: 100 [INFO] (ex22_main.c:33) THE_SIZE is: 1000 [INFO] (ex22.c:32) I think size is: 1000 [INFO] (ex22_main.c:38) THE SIZE is now: 9 [INFO] (ex22.c:32) I think size is: 9 [INFO] (ex22_main.c:42) Ratio at first: 1.000000 [INFO] (ex22_main.c:43) Ratio again: 2.000000 [INFO] (ex22_main.c:44) Ratio once more: 10.000000 [INFO] (ex22_main.c:8) count is: 4 [INFO] (ex22_main.c:16) count is at exit: 4 [INFO] (ex22_main.c:20) count after assign: 3000 [INFO] (ex22_main.c:8) count is: 80 [INFO] (ex22_main.c:13) count in this scope is 100 [INFO] (ex22_main.c:16) count is at exit: 80 [INFO] (ex22_main.c:20) count after assign: 3000 [INFO] (ex22_main.c:51) count after calling scope_demo: 4 Make sure you trace how each variable is changing and match it to the line that gets output. I'm using log_info from the dbg.h macros so you can get the exact line number where each variable is printed and find it in the files for tracing. ======Scope, Stack, And Bugs====== If you've done this right you should now see many of the different ways you can place variables in your C code. You can use extern or access functions like get_age to create globals. You can make new variables inside any blocks, and they'll retain their own values until that block exits, leaving the outer variables alone. You also can pass a value to a function, and change the parameter but not change the caller's version of it. The most important thing to realize though is that all of this causes bugs. C's ability to place things in many places in your machine and then let you access it in those places means you get confused easily about where something lives. If you don't where it lives then there's a chance you'll not manage it properly. With that in mind, here's some rules to follow when writing C code so you avoid bugs related to the stack: * Do not "shadow" a variable like I've done here with count in scope_demo. It leaves you open to subtle and hidden bugs where you think you're changing a value and you actually aren't. * Avoid too many globals, especially if across multiple files. If you have to then use accessor functions like I've done with get_age. This doesn't apply to constants, since those are read-only. I'm talking about variables like THE_SIZE. If you want people to modify or set this, then make accessor functions. * When in doubt, put it on the heap. Don't rely on the semantics of the stack or specialized locations and instead just create things with malloc. * Don't use function static variables like I did in update_ratio. They're rarely useful and end up being a huge pain when you need to make your code concurrent in threads. They are also hard as hell to find compared to a well done global variable. * Avoid reusing function parameters as it's confusing whether you're just reusing it or if you think you're changing the caller's version of it. As with all things, these rules can be broken when it's practical. In fact, I guarantee you'll run into code that breaks all of these rules and is perfectly fine. The constraints of different platforms makes it necessary sometimes. ======How To Break It====== For this exercise, breaking the program involves trying to access or change things you can't: * Try to directly access variables in ex22.c from ex22_main.c that you think you can't. For example, you can't get at ratio inside update_ratio? What if you had a pointer to it? * Ditch the extern declaration in ex22.h to see what you get for errors or warnings. * Add static or const specifiers to different variables and then try to change them. ======Extra Credit====== * Research the concept of "pass by value" vs. "pass by reference". Write an example of both. * Use pointers to gain access to things you shouldn't have access to. * Use valgrind to see what this kind of access looks like when you do it wrong. * Write a recursive function that causes a stack overflow. Don't know what a recursive function is? Try calling scope_demo at the bottom of scope_demo itself so that it loops. * Rewrite the Makefile so that it can build this. Copyright (C) 2010 Zed. A. Shaw Credits