summaryrefslogtreecommitdiff
path: root/reference/C/CONTRIB/SAWTELL/c-lesson.9
blob: 5d03e3baee2890373553b1f44fb2477d0be3926a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433

                                 Lesson 8.

  This lesson and the following one will examine how to use the program
structure - as opposed to data structure - reserved words.

  Lets start with the looping structures:

  do repeated_statement while ( logical_expression );

  repeated_statement, which may be a block of code, will be executed
repetitively until the logical_expression, becomes false. If you have been
exposed to ( corrupted by? ) another language remember that there is no
`until' test at the end of a loop. Note that the repeated_statement is always
executed once irrespective of the state of the logical_expression.

  while ( logical_expression ) repeated_statement;

  repeated_statement is executed repetitively while the logical_expression
is true. Once again statement may be a block of code. Note that if the
logical_expression evaluates to FALSE then the repeated_statement is NEVER
executed.

  Associated with the looping structures are the control words:

  break;
  continue;

  break; allows you to leave a loop in the middle of a block, and
  continue; allows you to re-start it from the top.

  Finally we must not forget the most common and useful looping construct:

  for ( initialising statement; logical_expression; incremental_statement )
  repeated_statement;

  Some further explanation is needed. The initialising statement is
executed once, but to allow for the need to initialise several separate
variables the assignment statements may be separated by commas. The
logical_expression must be true for the loop to run, and the
incremental_statement is executed once each time the loop is run.
The for statement is completely general and may, for example, be used to
manipulate a set of pointers to operate on a linked list.

Some examples.

  A do loop program.

#ident "@(#) do_demo.c - An example of the do loop"

#include <stdio.h>

main()
{
  char character;

  character = 'a';

  do printf ( "%c", character ); while ( character++ < 'z' );
  printf ( "\n" );
  }

  Fairly obviously it prints:

abcdefghijklmnopqrstuvwxyz

  A while loop example.

#ident "@(#) while_demo.c - An example of the while loop"

#include <stdio.h>

main()
{
  char character;

  character = 'a';

  while ( character <= 'z' ) printf ( "%c", character++ );
  printf ( "\n" );
  }

  Its output is exactly the same as the previous example:

abcdefghijklmnopqrstuvwxyz

  In this totally trivial case it is irrelevant which program structure
  you use, however you should note that in the `do' program structure the
  repeated statement is always executed at least once.
  A for loop example.

  The `for' looping structure.

#ident "@(#) for_demo.c - An example of the for loop"

#include <stdio.h>

main()
{
  char character;

  for ( character = 'a'; character <= 'z' ; character++ )
  {
    printf ( "%c", character );
    }
  printf ( "\n" );
  }

  Surprise, Surprise!

abcdefghijklmnopqrstuvwxyz

  You should be aware that in all the looping program structures, the
repeated statement can be a null statement ( either just a `;' or the
reserved word `continue;' ). This means that it is possible to - for
example - position a pointer, or count up some items of something or other.
It isn't particularly easy to think up a trivial little program which
demonstrates this concept, however the two `for' loops give some indication
of the idea.

#ident "@(#) pointer_demo.c - Pointer operations with the for loop"

#include <stdio.h>

main()
{
  char character, *character_pointer, alphabets [ 53 ];

  for ( character = 'a', character_pointer = alphabets;  /* Start conditions */
        character <= 'z';                                /* Run while true   */
        *character_pointer++ = character++               /* All the work     */
        )TRUE continue;

  for ( character = 'A';  /* character_pointer is at the right place already */
        character <= 'Z';
        *character_pointer++ = character++
        ) continue;

  *character_pointer = (char) '\000'; /* NULL character to terminate string. */

  printf ( "%s\n\n", alphabets );
  }

  Another Surprise!

abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ

  So much for the looping structures provided by the `C' language. The
other main structures required to program a computer are the ones which
alter the program flow. These are the switch, and the if and its extension
the if ... else combination. More demo programs are much the best way of
getting the message across to you, so here they are, first the if construct.

#ident "if_demo.c"

#include <stdio.h>

main(argc, argv)
int argc;
char **argv;
{
  if ( argc > 1 ) printf ( "You have initiated execution with arguments."};
  }

  And the if ... else demo.

#ident "if_else_demo.c"
/*
** The Language #define could go in the compiler invocation line if desired.
*/

#define ENGLISH

#include <stdio.h>

/*
** The message and text fragments output by the program.
*/

char *messages[] =
{
#if defined( ENGLISH )
#ident "@(#)ENGLISH Version"
  "\nUsage: if_else_demo <numeric argument 1> <numeric argument 2>\n\n",
  "The first argument is ",
  "the second",
  "equal to ",
  "bigger than ",
  "smaller than "
#endif

#if defined( FRANCAIS )
#ident "@(#)FRENCH Version"

  put the French translation in here so that we are ready to export to
  French speaking Countries. I'd be grateful if a French speaker could
  make the translation for me.

#endif
  };

/*
** Meaningful words defined to constants
*/

#define USAGE 0
#define FIRST 1
#define SECOND 2
#define EQUAL 3
#define BIGGER 4
#define SMALLER 5

#define SUCCESS 0
#define FAILURE 1

/*
** We need this more than once so it can be put in a function.
*/

void usage()
{
  printf ( messages[USAGE]);
  exit ( FAILURE );
  }

/*
** Main program function starts here. ( At the top of a page no less! )
*/

int main ( argc, argv )
int argc;
char **argv;
{
  int message_index;
  double i, j, strtod();
  char *ptr;

  if ( argc != 3 ) usage();                  /* have we been given the right */
                                             /* number of arguments. */
  i = strtod ( argv[1], &ptr);               /* Convert to a double float. */
  if ( ptr == argv[1] ) usage();             /* Successful conversion? */
  j = strtod ( argv[2], &ptr);               /* Convert to a double float. */
  if ( ptr == argv[2] ) usage();             /* Successful conversion? */

/*
** This statement uses the "ternary conditional assignment" language
** construction to assign the value required to the message indexing variable.
** Note that this concept is efficient in both the generation of machine code
** output ( compile the program with a -S switch and have a look ) and in the
** ease with which it can be understood. The assignment is obvious instead of
** being buried under a litter of `if' and `else' keywords.
*/

  message_index = ( i == j ) ? EQUAL : ( i > j ) ? BIGGER : SMALLER;

/*
** Now print the message.
*/

  (void) printf ( "\n%s%s%s\n\n",     /* Format string specifying 3 strings. */
                  messages[ FIRST ],                   /* Address of string. */
                  messages[ message_index ],           /*        ditto.      */
                  messages[ SECOND ]                   /*        ditto.      */
                  );
  return ( SUCCESS );
  }

  Well as you can no doubt gather it simply compares two numbers on the
command line and ejects a little message depending on the relative magnitude
of the numbers. In the UNIX tradition the help message is perhaps somewhat
terse, but it serves the purpose of getting you - the student - to think
about the importance of creating programs which always cope with nonsensical
input in a civilised way. Here are the lines of output.

Usage: if_else_demo <numeric argument 1> <numeric argument 2>

The first argument is equal to the second

The first argument is smaller than the second

The first argument is bigger than the second

  Now that the international community is shrinking with vastly improved
telecommunications, it is perhaps a good idea to think carefully about
creating programs which can talk in many languages to the users. The method
of choice is - I believe - that presented above. The #if defined( LANGUAGE )
gives us an easy method of changing the source code to suit the new sales
area. Another possibility is to put all the text output needed from a program
into a file. The file would have to have a defined layout and some consistent
way of `getting at' the message strings.

  From a commercial point of view this may or may not be a good business plan.
Quite definitely it is an absolute no no to scatter a mass of string literals
containing the messages and message fragments all over your program script.

  There are two more methods of altering the program flow.

  1 ) The goto a label.
  2 ) The setjump / longjmp library routines.

  The concept of the go to a label construction has had reams of literary
verbiage written about it and this author does not intend to add to the pile.
Suffice it to say that a goto is a necessary language construct. There are a
few situations which require the language to have ( in practice ) some form of
unconditional jump. Treat this statement with great caution if you wish your
code to be readable by others. An example of legitimate use.

  for ( a = 0; a < MATRIX_SIZE; a++ )
  {
    for ( b = 0; b < MATRIX_SIZE; b++ )
    {
      if ( process ( matrix, a, b )) goto bad_matrix;
      }
     }
   return ( OK );

bad_matrix:

   perror ( progname, "The data in the matrix seems to have been corrupted" );
   return ( BAD );

  This is one of the very few "legitimate" uses of goto, as there is no
"break_to_outer_loop" in `C'. Note that some compilers complain if the label
is not immediately followed by a statement. If your compiler is one of these
naughty ones, you can put either a `;' or a pair of braces `{}' after the
`:' as a null statement.

  An example of a program package which makes extensive use of the goto is the
rz and sz modem communications protocol implementation by Chuck Forsberg of
Omen Technology. You should download it and study the code, but do remember
that the proof of the pudding argument must apply as the rz & sz system has
become extremely popular in its application because it works so well.

  The other method of changing program flow is the setjump and longjmp pair of
library functions. The idea is to provide a method of recovery from errors
which might be detected anywhere within a large program - perhaps a compiler,
interpreter or large data acquisition system. Here is the trivial example:

#ident "set_jmp_demo.c"

#include <stdio.h>
#include <setjmp.h>

jmp_buf save;

main()
{
  char c;

  for ( ;; )                     /* This is how you set up a continuous loop.
*/
  {
    switch ( setjmp( save ))
    {
case 0:
      printf ( "We get a zero returned from setjmp on setup.\n\n");
      break;                   /* This is the result from setting up. */

case 1:
      printf ( "NORMAL PROGRAM OPERATION\n\n" );
      break;

case 2:
      printf ( "WARNING\n\n" );
      break;

case 3:
      printf ( "FATAL ERROR PROGRAM TERMINATED\n\nReally Terminate? y/n: " );
      fflush ( stdout );
      scanf ( "%1s", &c );
      c = tolower ( c );
      if ( c == 'y' ) return ( 1 );
      printf ( "\n" );
      break;

default:
      printf ( "Should never return here.\n" );
      break;
      }
    process ();
    }
  }

process ()
{
  int i;

  printf ( "Input a number to simulate an error condition: " );
  fflush ( stdout );
  scanf ( "%d", &i );
  i %= 3;
  i++;                /* So that we call longjmp with  0 < i < 4 */
  longjmp ( save, i);
  }

  Although in this silly little demo the call to longjmp is in the same file
as the call to setjmp, this does not have to be the case, and in the practical
situation the call to longjmp will be a long way from the call to setjmp. The
mechanism is that setjmp saves the entire state of the computer's CPU in a
buffer declared in the jmp_buf save; statement and longjmp restores it exactly
with the exception of the register which carries the return value from longjmp.
This value is the same as the second argument in the longjmp call - i in our
little demo. This means, of course, that the stack and frame pointer registers
are reset to the old values and all the local variables being used at the time
of the longjmp call are going to be lost forever. One consequence of this is
that any pointer to memory allocated from the heap will also be lost, and
you will be unable to access the data stored in the buffer. This is what the
jargonauts call "memory leakage", and is really very difficult bug to find.
Your program runs out of dynamic memory long before it should. Take care.
So you have to keep a record of the buffers' addresses and free them
before the call to longjmp.

More details later on when we learn about the heap memory allocation routines.

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 )   |
 +----------------------------------------------------------------------+