diff options
author | Tobias Klauser <tklauser@distanz.ch> | 2008-01-27 11:37:44 +0100 |
---|---|---|
committer | Tobias Klauser <tklauser@xenon.tklauser.home> | 2008-01-27 11:37:44 +0100 |
commit | 7e0f021a9aec35fd8e6725e87e3313b101d26f5e (patch) | |
tree | b1cacc4b24393f517aeb4610e9e1021f954307a8 /reference/C/CONTRIB/SAWTELL/c-lesson.8 |
Initial import (2.0.2-6)2.0.2-6
Diffstat (limited to 'reference/C/CONTRIB/SAWTELL/c-lesson.8')
-rwxr-xr-x | reference/C/CONTRIB/SAWTELL/c-lesson.8 | 502 |
1 files changed, 502 insertions, 0 deletions
diff --git a/reference/C/CONTRIB/SAWTELL/c-lesson.8 b/reference/C/CONTRIB/SAWTELL/c-lesson.8 new file mode 100755 index 0000000..8e2bff9 --- /dev/null +++ b/reference/C/CONTRIB/SAWTELL/c-lesson.8 @@ -0,0 +1,502 @@ + + Lesson 7. + + De-bugging Strategies. + + >>>>>>>> Proper Preparation Prevents Piss-Poor Performance. <<<<<<<< + + This lesson is really a essay about how to go about writing programs. + + I know that by far the best way to greatly reduce the amount of effort +required to get a program going properly is to avoid making mistakes in the +first palace! Now this might seem to be stating the absolute obvious, and it +is but after looking at many programs it would seem that there is a very +definite need to say it. + + So how does one go about reducing the probability of making mistakes? + + There are many strategies, and over the years I have evolved my own set. + I have found that some of the most important are: + + 1) Document what you are going to do before yes BEFORE you write any code. + Set up the source files for the section of the program you are going to + write and put some lines of explanation as to what you intend to do in + this file. Be as precise as you can, but don't go into the detail of + explaining in English, or your First Language, exactly what every + statement does. + + 2) Make sure that you keep each file as small as is sensible. Some program + authors say that one should put only one function in a file. It's my + personal opinion that this is going a little bit over the top, but + certainly you should not have more than one logical activity in a source + file. It's easier to find a needle in a tiny haystack than in a big one! + + 3) Always use names for the objects in your program which are fully + descriptive, or at the very least are meaningful nmemonics. Put yourself + in the position of some poor soul who - a couple of years later, after you + have long finished with the project, and left the country - has been given + the task of adding a small feature to your exquisite program. Now in the + rush to get your masterpiece finished you decided to use variable names + like "a4" and "isb51" simply so that you can get the line typed a + fraction of a second faster than if you used something like + "customer_address[POST_CODE]" and "input_status_block[LOW_FUEL_TANK_#3]. + The difference in ease of understanding is obvious, isn't it? However + judging by some programs which I have seen published in both magazines and + in the public domain program sources, the point has still to be made. + + 4) ALWAYS take great care with the layout of your code. + It's my opinion that the opening brace of ALL program structures should + be on a new line. Also if you put them in the leftmost column for structs, + enums, and initialised tables, as well as functions, then the + 'find function' keystrokes ( "[[" and "]]" ) in vi will find them as well + as the functions themselves. Make sure you have the "showmatch" facility + in vi turned on. ( And watch the cursor jump when you enter the + right hand brace, bracket, or parenthesis. ) + + 5) Try as hard as you can to have as few global variables as possible. + Some people say never have any globals. This is perhaps a bit too + severe but global variables are a clearly documented source of + programming errors. If it's impossible to perform a logical activity + in an efficient way without having a global or two, then confine + the scope of the globals to just the one file by marking the defining + declaration "static". This stops the compiler producing a symbol which + the linking loader will make available to all the files in your source. + + 6) Never EVER put 'magic numbers' in you source code. Always define constants + in a header file with #define lines or enum statements. + + Here is an example:- + + +/* ----------------------------------------- */ + +#include <stdio.h> + +enum status_input_names +{ + radiator_temperature, + oil_temperature, + fuel_pressure, + energy_output, + revolutions_per_minute + }; + +char *stats[] = +{ + "radiator_temperature", + "oil_temperature", + "fuel_pressure", + "energy_output", + "revolutions_per_minute" + }; + +#define NUMBER_OF_INPUTS ( sizeof ( stats ) / sizeof ( stats[0])) + +main() +{ + enum status_input_names name; + + printf ( "Number of Inputs is: %d\n", NUMBER_OF_INPUTS ); + for ( name = radiator_temperature; name < NUMBER_OF_INPUTS; name++) + { + printf ( "\n%s", stats[ name ] ); + } + printf ( "\n\n" ); + } + +/* ----------------------------------------- */ + + Note that as a side effect we have available the meaningful symbols + radiator_temperature etc. as indices into the array of status input names + and the symbol NUMBER_OF_INPUTS available for use as a terminator in the + 'for' loop. This is quite legal because sizeof is a pseudo-function and the + value is evaluated at the time of compilation and not when the program is + executed. This means that the result of the division in the macro is + calculated at the time of compilation and this result is used as a literal + in the 'for' loop. No division takes place each time the loop is executed. + + To illustrate the point I would like to tell you a little story which is + fictitious, but which has a ring of truth about it. + Your employer has just landed what seems to be a lucrative contract with + an inventor of a completely new type of engine. We are assured that after + initial proving trials one of the larger Japanese motor manufactures is + going to come across with umpteen millions to complete the development of + the design. You are told to write a program which has to be a simple and + straightforward exercise in order to do the job as cheaply as possible. + Now, the customer - a some-what impulsive type - realises that his + engine is not being monitored closely enough when it starts to rapidly + dis-assemble itself under high speed and heavy load. You have to add a + few extra parameters to the monitoring program by yesterday morning! + You just add the extra parameters into the enumand the array of pointers + to the character strings. So: + +enum status_input_names +{ radiator_temperature, + radiator_pressure, + fuel_temperature, + fuel_pressure, + oil_temperature, + oil_pressure, + exhaust_manifold_temperature + }; + + Let's continue the story about the Japanese purchase. Mr. Honda ( jun ) has + come across with the money and the result is that you are now a team leader + in the software section of Honda Software ( YourCountry ) Ltd. The project of + which you are now leader is to completely rewrite your monitoring program and + add a whole lot of extra channels as well as to make the printouts much more + readable so that your cheap, cheerful, and aesthetic-free program can be sold + as the "Ultimate Engine Monitoring Package" from the now world famous Honda + Real-time Software Systems. You set to work, Honda et. al. imagine that there + is going to be a complete redesign of the software at a cost of many million + Yen. You being an ingenious type have written the code so that it is easy to + enhance. + + The new features required are that the printouts have to be printed with the + units of measure appended to the values which have to scaled and processed so + that the number printed is a real physical value instead of the previous + arrangement where the raw transducer output was just dumped onto a screen. + + What do you have to do? + + Thinking along the line of "Get the Data arranged correctly first". + You take you old code and expand it so that all the items of information + required for each channel are collected into a struct. + +enum status_input_names +{ + radiator_temperature, + radiator_pressure, + fuel_temperature, + fuel_pressure, + oil_temperature, + oil_pressure, + exhaust_manifold_temperature, + power_output, + torque + }; + +typedef struct channel +{ + char *name; /* Channel Name to be displayed on screen. */ + int nx; /* position of name on screen x co-ordinate. +*/ + int ny; /* ditto for y */ + int unit_of_measure; /* index into units of measure array */ + char value; /* raw datum value from 8 bit ADC */ + char lower_limit; /* For alarms. */ + char upper_limit; + float processed_value; /* The number to go on screen. */ + float offset; + float scale_factor; + int vx; /* Position of value on screen. */ + int vy; + }CHANNEL; + +enum units_of_measure { kPa, degC, kW, rpm, Volts, Amps, Newtons }; + +char *units { "kPa", "degC", "kW", "rpm", "Volts", "Amps", "Newtons" }; + +CHANNEL data [] = +{ + { "radiator temperature", + { "radiator pressure", + { "fuel temperature", + { "fuel pressure", + { "oil temperature", + { "oil pressure", + { "exhaust manifold temperature", + { "power output", + { "torque", + }; + +#define NUMBER_OF_INPUTS sizeof (data ) / sizeof ( data[0] ) + +Now the lesson preparation is to find the single little bug in the above +program fragment, to finish the initialisation of the data array of type +CHANNEL and to have a bit of a crack at creating a screen layout +program to display its contents. Hint: Use printf(); +( Leave all the values which originate from the real world as zero. ) + + + Here are some more tips for young players. + + 1) Don't get confused between the logical equality operator, + + == + + and the assignment to a variable operator. + + = + + This is probably the most frequent mistake made by 'C' beginners, and + has the great disadvantage that, under most circumstances, the compiler + will quite happily accept your mistake. + + 2) Make sure that you are aware of the difference between the logical + and bit operators. + + && This is the logical AND function. + || This is the logical OR function. + The result is ALWAYS either a 0 or a 1. + + & This is the bitwise AND function used for masks etc. + The result is expressed in all the bits of the word. + + 3) Similarly to 2 be aware of the difference between the logical + complementation and the bitwise one's complement operators. + + ! This is the logical NOT operator. + ~ This is the bitwise ones complement op. + + Some further explanation is required. In deference to machine efficiency a + LOGICAL variable is said to be true when it is non-zero. So let's set a + variable to be TRUE. + + 00000000000000000000000000000001 A word representing TRUE. + Now let's do a logical NOT !. + 00000000000000000000000000000000 There is a all zero word, a FALSE. + + 00000000000000000000000000000001 That word again. TRUE. + Now for a bitwise complement ~. + 11111111111111111111111111111110 Now look we've got a word which is + non-zero, still TRUE. + + Is this what you intended? + + 4) It is very easy to fall into the hole of getting the + '{' & '}'; '[' & ']'; '(' & ')'; symbol pairs all messed up and the + computer thinks that the block structure is quite different from that + which you intend. Make sure that you use an editor which tells you the + matching symbol. The UNIX editor vi does this provided that you turn + on the option. Also take great care with your layout so that the block + structure is absolutely obvious, and whatever style you choose do take + care to stick by it throughout the whole of the project. + A personal layout paradigm is like this: + + Example 1. + +function_type function_name ( a, b ) +type a; +type b; +{ + type variable_one, variable_two; + + if ( logical_expression ) + { + variable_one = A_DEFINED_CONSTANT; + if ( !return_value = some_function_or_other ( a, + variable_one, + &variable_two + ) + ) + { + error ( "function_name" ); + exit ( FAILURE ); + } + else + { + return ( return_value + variable_two ); + } + } /* End of "if ( logical_expression )" block */ + } /* End of function */ + + This layout is easy to do using vi with this initialisation script + in either the environment variable EXINIT or the file ${HOME}/.exrc:- + +set showmode autoindent autowrite tabstop=2 shiftwidth=2 showmatch wm=1 + + Example 2. + +void printUandG() +{ + char *format = +"\n\ + User is: %s\n\ + Group is: %s\n\n\ + Effective User is: %s\n\ +Effective Group is: %s\n\n"; + + ( void ) fprintf ( tty, + format, + passwd_p->pw_name, + group_p->gr_name, + epasswd_p->pw_name, + egroup_p->gr_name + ); + } + + Notice how it is possible to split up format statements with a '\' as + the last character on the line, and that it is convenient to arrange + for a nice output format without having to count the + field widths. Note however that when using this technique that the '\' + character MUST be the VERY LAST one on the line. Not even a space may + follow it! + + In summary I *ALWAYS* put the opening brace on a new line, set the tabs + so that the indentation is just two spaces, ( use more and you very quickly + run out of "line", especially on an eighty column screen ). If a statement + is too long to fit on a line I break the line up with the arguments set out + one to a line and I then the indentation rule to the parentheses "()" + as well. Sample immediately above. Probably as a hang-over from a particular + pretty printing program which reset the indentation position after the + printing of the closing brace "}", I am in the habit of doing it as well. + Long "if" and "for" statements get broken up in the same way. This is + an example of it all. The fragment of code is taken from a curses oriented + data input function. + + /* + ** Put all the cursor positions to zero. + */ + + for ( i = 0; + s[i].element_name != ( char *) NULL && + s[i].element_value != ( char *) NULL; + i = ( s[i].dependent_function == NULL ) + ? s[i].next : s[i].dependent_next + ) + { /* Note that it is the brace and NOT the */ + /* "for" which moves the indentation level. */ + s[i].cursor_position = 0; + } + + /* + ** Go to start of list and hop over any constants. + */ + + for ( i = edit_mode = current_element = 0; + s[i].element_value == ( char *) NULL ; + current_element = i = s[i].next + ) continue; /* Note EMPTY statement. */ + + /* + ** Loop through the elements, stopping at end of table marker, + ** which is an element with neither a pointer to an element_name nor + ** one to a element_value. + */ + + while ( s[i].element_name != ( char *) NULL && + s[i].element_value != ( char *) NULL + ) + { + int c; /* Varable which holds the character from the keyboard. */ + + /* + ** Et Cetera for many lines. + */ + + } + + Note the commenting style. The lefthand comments provide a general +overview of what is happening and the righthand ones a more detailed view. +The double stars make a good marker so it is easy to separate the code and +the comments at a glance. + + The null statement. + + You should be aware that the ";" on its own is translated by the compiler +as a no-operation statement. The usefullness of this is that you can do +little things, such as counting up a list of objects, or positioning a pointer +entirely within a "for" or "while" statement. ( See example above ). +There is, as always, a flip side. It is HORRIBLY EASY to put a ";" at the +end of the line after the closing right parenthesis - after all you do just +that for function calls! The suggestion is to both mark deliberate null +statements with a comment and to use the statement "continue;". Using +the assert macro will pick up these errors at run time. + + The assert macro. + + Refer to the Programmers Reference Manual section 3X and find the +documentation on this most useful tool. + + As usual an example is by far the best wasy to explain it. + +/* ----------------------------------------- */ + +#ident "@(#) assert-demo.c" + +#include <stdio.h> +#include <assert.h> + +#define TOP_ROW 10 +#define TOP_COL 10 + +main() +{ + int row, col; + + for ( row = 1; row <= TOP_ROW; row++); + { + assert ( row <= TOP_ROW ); + for ( col = 1; col <= TOP_COL; col++ ) + { + assert ( col <= TOP_COL ); + printf ( "%4d", row * col ); + } + printf ( "\n" ); + } + } + +/* ----------------------------------------- */ + + Which produces the output:- + +Assertion failed: row <= TOP_ROW , file assert-demo.c, line 15 +ABORT instruction (core dumped) + + It does this because the varable "row" is incremented +to one greater than The value of TOP_ROW. + + Note two things: + + 1) The sense of the logical condition. The assert is asserted + as soon as the result of the logical condition is FALSE. + Have a look at the file /usr/include/assert. + Where is the ";" being used as an empty program statement? + + 2) The unix operating system has dumped out an image of the executing + program for examination using a symbolic debugger. Have a play with + "sdb" in preparation for the lesson which deals with it in more + detail. + + Lets remove the errant semi-colon, re-compile and re-run the program. + + 1 2 3 4 5 6 7 8 9 10 + 2 4 6 8 10 12 14 16 18 20 + 3 6 9 12 15 18 21 24 27 30 + 4 8 12 16 20 24 28 32 36 40 + 5 10 15 20 25 30 35 40 45 50 + 6 12 18 24 30 36 42 48 54 60 + 7 14 21 28 35 42 49 56 63 70 + 8 16 24 32 40 48 56 64 72 80 + 9 18 27 36 45 54 63 72 81 90 + 10 20 30 40 50 60 70 80 90 100 + + Here's the ten times multiplication table, for you to give to to +the nearest primary-school child! + + I would agree that it is not possible to compare the value of a program +layout with a real work of fine art such as a John Constable painting or +a Michaelangelo statue, I do think a well laid out and literate example of +programming is not only much easier to read and understand, but also it +does have a certain aesthetic appeal. + +Copyright notice:- + +(c) 1993 Christopher Sawtell. + +I assert the right to be known as the author, and owner of the +intellectual property rights of all the files in this material, +except for the quoted examples which have their individual +copyright notices. Permission is granted for onward copying, +but not modification, of this course and its use for personal +study only, provided all the copyright notices are left in the +text and are printed in full on any subsequent paper reproduction. + +-- + +----------------------------------------------------------------------+ + | NAME Christopher Sawtell | + | SMAIL 215 Ollivier's Road, Linwood, Christchurch, 8001. New Zealand.| + | EMAIL chris@gerty.equinox.gen.nz | + | PHONE +64-3-389-3200 ( gmt +13 - your discretion is requested ) | + +----------------------------------------------------------------------+ |