Journal Articles

CVu Journal Vol 16, #1 - Feb 2004 + Programming Topics
Browse in : All > Journals > CVu > 161 (8)
All > Topics > Programming (877)
Any of these categories - All of these categories

Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.

Title: do...while

Author: Site Administrator

Date: 07 February 2004 19:37:48 +00:00 or Sat, 07 February 2004 19:37:48 +00:00

Summary: 

What can be said about C’s everyday do...while loop? It just does something while some condition holds. End of story, right?

No, of course not. That would make the title of this small article silly, so let’s cover two topics.

Body: 

Firstly, the description above of what do...while does is wrong, for one simple reason. do...while always does its *something* at least once. It would be better described as:

“do the thing once, and then continue doing it as long as some condition is met”.

That added complexity makes formal reasoning about programs using do...while more difficult; their invariants can easily end up being of the form:

“x always holds, and y is guaranteed to hold except for the first time around”.

A while loop, by testing its condition at the top of the loop, gives simple invariants. This isn’t just abstract nonsense (we’re not talking about category theory here). With practice, designing programs around while loops as the default choice really does make for simpler, more robust code. Only when you find the code looks like:

do_x();
while (condition()) {
  do_x();
}

should you consider re-writing it as the equivalent do...while loop:

do {
  do_x();
} while (condition());

Secondly, let’s talk about idioms. Actually, about idioms and anti-idioms. To get the audience on my side, I’ll say that I don’t like code using goto statements. I might accept that one time in a million they provide a real advantage, but to me the consistency of a simple, absolute ban on their use pays off many times over. So, we’re all together here, no goto statements allowed.

Now for the anti-idiom. On hearing that coding standards were commonly disallowing goto statements, some ingenious programmers found a way around the restriction (at least for coding standards that didn’t also ban breaking out of loops). Here’s their trick to write what is in effect a goto without typing ‘g’, ‘o’, ‘t’, ‘o’:

do {
  function1();
  if (condition1())
    break;
  function2();
  if (condition2())
    break;
  function3();
} while (FALSE);

(assuming, of course, that FALSE is a constant evaluating to 0). Hang on a minute... do while(FALSE) sounds a bit like if (FALSE) – a way to stop code running. Except that (did I mention this above?) do...while always executes its body at least once. So do while(FALSE) is an odd way of saying “please run this statement at least once, and not more than once”. In other words “please run this (possibly compound) statement”. In this situation do...while is not being used as a loop, but only so that break statements can be used to simulate gotos. Restricted, forward-only gotos, to be sure, but they’re still gotos. When you write code using a control structure such as while, for or do...while intended for looping, make sure that looping is the concept you really wanted to communicate to the (mostly human) readers of your code. Don’t use do...while when you mean goto.

Except in one situation. Take a look at the following code, which defines a function-like macro (by the name of MACRO, to showcase my imagination) which is intended to be used like a function. Here is the idiomatic use of do while(FALSE).

#include <stdlib.h>
#include <stdio.h>

#if defined DEFINE_A_BROKEN_MACRO
#define MACRO(x) \
{ printf("Bad"); printf("\n"); }
#else
#define MACRO(x) \
do { printf("Good"); printf("\n");} \
while (0)
#endif

int main() {
  if (0)
    MACRO(x);
  else
    MACRO(y);
  return EXIT_SUCCESS;
}

(To the pedants: yes, I know that the return statement is optional according to the current C standard, but (a) almost no compilers yet implement the current C standard, and (b) it’s good to be explicit. That’s also why I write:

return EXIT_SUCCESS;

instead of just:

return 0;

even though any programmer competent with C should know that returning 0 from main is another way of reporting the successful exit status of a program. It’s more explicit.)

This program fails to compile if the first MACRO definition is used, but compiles quite happily if the second is used. To use the “broken” macro successfully, one of two strategies can be followed. The first is to omit the semi-colon in the

MACRO(x);

lines, making them read

MACRO(x)

which looks somewhat unnatural. The second is to stick to a rule that says that you always use { } in your if statements. If that is done, so that the if...else loop reads

if (0) {
  MACRO(x);
} else {
  MACRO(y);
}

then either of the macro definitions will work without problems.

Notes: 

More fields may be available via dynamicdata ..