summaryrefslogtreecommitdiff
path: root/reference/C/CONTRIB/SAWTELL/c-lesson.8
diff options
context:
space:
mode:
Diffstat (limited to 'reference/C/CONTRIB/SAWTELL/c-lesson.8')
-rwxr-xr-xreference/C/CONTRIB/SAWTELL/c-lesson.8502
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 ) |
+ +----------------------------------------------------------------------+