C Style (goto or not goto, that is the question)

Isaac Dimitrovsky dimitrov at csd2.UUCP
Fri Sep 27 09:55:00 AEST 1985


[]
Someone asked for a readable version of the following code:
>[example reformatted a bit]
>
>	noecho();
>retry:
>	move(4,10);
>	refresh();
>	ch = getch();
>	if ((ch < '1' || ch > '5') && ch != 'E') {
>		putchar(BELL);
>		goto retry;
>	}
>	addch(ch);
>	refresh();

OK, I've now read enough different responses on this to want to finish this
topic off once and for all. This code is an example of the general pattern:

	repeat
		do stuff;
		if termination condition has not been satisfied then
			give error indication;
	until the termination condition becomes true;

If the stuff to be done is very simple, this can be tersely
expressed in C as follows:

	while (combine doing stuff and check that termination condition is
			not true into one messy expression)
		give error indication;

or, more readably, as:

	for (do stuff; termination condition is not true; do stuff)
		give error indication;

For example, if we assume that the retry label in the code above is
not jumped to from anywhere else, then we could move it down two lines
to just before ch=getch() (the calls to move and refresh should not be
needed since echoing is turned off with noecho). This assumption is made
in all the goto-less versions of this code segment that I've seen.
Then the "stuff" to be done just reduces to the assignment ch=getch(),
and so the code segment can be written tersely as:

	noecho();
	move(4,10);
	refresh();
	while (((ch=getch())<'1' || ch>'5') && ch!='E')
		putchar(BELL);
	addch(ch);
	refresh();

or, the while loop could be rewritten:

	for (ch=getch(); ((ch<'1' || ch>'5') && ch!='E'); ch=getch())
		putchar(BELL);

If the stuff to be done is more complicated, these two forms quickly
become unreadable. In this case, you can either combine the stuff to
be done as a separate function, and then use the above forms again,
or use a do-while loop:

	do {
		do stuff;
		if (termination condition is not true)
			give error indication;
	} while (termination condition is not true);

or, if the check for the termination condition is very expensive:

	do {
		do stuff;
		if (termination condition is not true)
			give error indication;
		else
			break;
	} while (TRUE);

For example, suppose the calls to move and refresh above *are* necessary
before each call to getch. The code above could then be rewritten:

mygetch() {
	move(4,10);
	refresh();
	return getch();
}

	noecho();
	while (((ch=mygetch())<'1' || ch>'5') && ch!='E')
		putchar(BELL);
	addch(ch);
	refresh();

or using a do-while loop:

#define invalid(ch) (((ch)<'1' || (ch)>'5') && (ch)!='E')

	noecho();
	do {
		move(4,10);
		refresh();
		ch=getch();
		if (invalid(ch))
			putchar(BELL);
	} while (invalid(ch));
	addch(ch);
	refresh();

or rewrite the do-while loop as:

	do {
		move(4,10);
		refresh();
		ch=getch();
		if (invalid(ch))
			putchar(BELL);
		else
			break;
	} while (TRUE);

Isaac Dimitrovsky
allegra!cmcl2!csd2!dimitrov   (l in cmcl2 is letter l not number 1)
251 Mercer Street, New York NY 10012     (212) 674-8652

... Hernandez steps in to face ... Orl ... HERchiiiser ... and it's a liiine
driive, deeeeep to the gap in left center ...	- Bob Murphy, Voice of the Mets



More information about the Comp.lang.c mailing list